diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c2b79901afbd..52011a6f191a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -15,6 +15,17 @@ body: **SECURITY RELATED?** Submit here: https://hackerone.com/curl + - type: markdown + attributes: + value: " + > [!IMPORTANT] + + > If you cannot understand or explain your work without using + Artificial Intelligence (AI) then do not file here. Do not paste + massive AI generated explanations. We accept the use of AI as long as + it is digestible. Please explain your issues or improvements briefly + and clearly in your own human voice." + - type: textarea id: reproducer attributes: diff --git a/.github/ISSUE_TEMPLATE/docs.yml b/.github/ISSUE_TEMPLATE/docs.yml index 1b60a597adf0..d0c3852183ab 100644 --- a/.github/ISSUE_TEMPLATE/docs.yml +++ b/.github/ISSUE_TEMPLATE/docs.yml @@ -14,6 +14,17 @@ body: Only file documentation bugs here! Ask questions on the mailing lists https://curl.se/mail/ + - type: markdown + attributes: + value: " + > [!IMPORTANT] + + > If you cannot understand or explain your work without using + Artificial Intelligence (AI) then do not file here. Do not paste + massive AI generated explanations. We accept the use of AI as long as + it is digestible. Please explain your issues or improvements briefly + and clearly in your own human voice." + - type: textarea id: source attributes: diff --git a/.github/labeler.yml b/.github/labeler.yml index 32117e710751..f7adaba865e6 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -39,6 +39,7 @@ authentication: - any-glob-to-all-files: "{\ CMake/FindGSS.cmake,\ CMake/FindLibgsasl.cmake,\ + docs/internals/CREDENTIALS.md,\ docs/libcurl/opts/CURLINFO_HTTPAUTH*,\ docs/libcurl/opts/CURLINFO_PROXYAUTH*,\ docs/libcurl/opts/CURLOPT_KRB*,\ @@ -49,10 +50,13 @@ authentication: docs/libcurl/opts/CURLOPT_XOAUTH*,\ lib/*gssapi*,\ lib/*ntlm*,\ + lib/creds.*,\ + lib/curl_ntlm*,\ lib/curl_sasl.*,\ lib/http_aws*,\ lib/http_digest.*,\ lib/http_negotiate.*,\ + lib/http_ntlm.*,\ lib/vauth/**\ }" @@ -129,6 +133,8 @@ connecting & proxies: docs/libcurl/opts/CURLOPT_SOCKS*,\ docs/libcurl/opts/CURLOPT_TCP*,\ docs/libcurl/opts/CURLOPT_TIMEOUT*,\ + lib/cf-https-connect.*,\ + lib/cf-ip-happy.*,\ lib/cf-*proxy.*,\ lib/cf-socket.*,\ lib/cfilters.*,\ @@ -136,9 +142,10 @@ connecting & proxies: lib/connect.*,\ lib/http_proxy.*,\ lib/if2ip.*,\ - lib/noproxy.*,\ + lib/proxy.*,\ lib/socks.*,\ src/tool_cb_soc.*,\ + tests/http/*proxy*,\ tests/http/*socks*,\ tests/server/socksd.c\ }" @@ -295,11 +302,14 @@ HTTP/3: .github/workflows/http3-linux.yml,\ CMake/FindNGHTTP3.cmake,\ CMake/FindNGTCP2.cmake,\ + docs/cmdline-opts/proxy-http3.md,\ docs/HTTP3.md,\ docs/examples/http3*,\ + lib/cf-h3-proxy.*,\ lib/vquic/**,\ tests/http3-server.pl,\ - tests/nghttpx.conf\ + tests/nghttpx.conf,\ + tests/http/*httpsrr*\ }" IMAP: @@ -381,6 +391,8 @@ name lookup: lib/socketpair*,\ lib/thrdpool.*,\ lib/thrdqueue.*,\ + tests/http/testenv/dnsd.*,\ + tests/http/*httpsrr*,\ tests/http/*resolve.py,\ tests/server/dnsd.c,\ tests/server/resolve.c\ @@ -458,6 +470,7 @@ tests: - all: - changed-files: - any-glob-to-any-file: + - 'docs/tests/**' - 'tests/**' TFTP: @@ -548,9 +561,9 @@ Windows: lib/curlx/fopen.*,\ lib/curlx/multibyte.*,\ lib/curlx/winapi.*,\ + lib/libcurl.def,\ lib/vtls/schannel*,\ m4/curl-schannel.m4,\ projects/Windows/**,\ - src/tool_doswin.c,\ - lib/libcurl.def\ + src/tool_doswin.c\ }" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000000..34e39a217218 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,8 @@ + diff --git a/.github/scripts/cleancmd.pl b/.github/scripts/cleancmd.pl index 988d4562046a..d6ddeb7274f4 100755 --- a/.github/scripts/cleancmd.pl +++ b/.github/scripts/cleancmd.pl @@ -16,7 +16,7 @@ my @asyms; open(S, "<./docs/libcurl/symbols-in-versions") - || die "cannot find symbols-in-versions"; + or die "cannot find symbols-in-versions"; while() { if(/^([^ ]*) /) { push @asyms, $1; @@ -30,7 +30,7 @@ ); open(O, "<./docs/options-in-versions") - || die "cannot find options-in-versions"; + or die "cannot find options-in-versions"; while() { chomp; if(/^([^ ]+)/) { @@ -50,7 +50,7 @@ close(O); open(C, "<./.github/scripts/spellcheck.curl") - || die "cannot find spellcheck.curl"; + or die "cannot find spellcheck.curl"; while() { if(/^\#/) { next; diff --git a/.github/scripts/cmp-config.pl b/.github/scripts/cmp-config.pl index 66cb65756325..9c353432bbfb 100755 --- a/.github/scripts/cmp-config.pl +++ b/.github/scripts/cmp-config.pl @@ -34,8 +34,7 @@ exit; } -# this lists complete lines that will be removed from the output if -# matching +# this lists complete lines that are removed from the output if matching my %remove = ( '#define CURL_EXTERN_SYMBOL' => 1, '#define CURL_OS "Linux"' => 1, diff --git a/.github/scripts/pyspelling.words b/.github/scripts/pyspelling.words index 0c986f14c6c2..1c0fb96d9b05 100644 --- a/.github/scripts/pyspelling.words +++ b/.github/scripts/pyspelling.words @@ -167,8 +167,10 @@ CWE cyassl Cygwin daniel +datagrams datatracker dbg +decapsulation Debian DEBUGBUILD decrypt @@ -234,6 +236,7 @@ EGD EHLO EINTR else's +encapsulation encodings enctype endianness @@ -896,6 +899,7 @@ trustless Tse Tsujikawa TTL +tty tvOS txt typedef @@ -1010,6 +1014,7 @@ Youtube YYYY YYYYMMDD Zakrzewski +Zeropath Zitzmann zlib zsh diff --git a/.github/scripts/randcurl.pl b/.github/scripts/randcurl.pl index f9c24d90db6c..2c24a6b5007b 100755 --- a/.github/scripts/randcurl.pl +++ b/.github/scripts/randcurl.pl @@ -61,7 +61,7 @@ sub storedata { } sub getoptions { - my @all = `$curl --help all`; + my @all = qx($curl --help all); for my $o (@all) { chomp $o; if($o =~ /^ -(.), --([^ ]*) (.*)/) { @@ -142,7 +142,7 @@ sub runone { $a .= " ".addarg(); } - my $cmd="$curl$a $url"; + my $cmd = "$curl$a $url"; my $rc = system("$cmd >curl-output 2>&1 > 8; #my $rc = system("valgrind -q $cmd >/dev/null 2>&1 > 8; @@ -194,7 +194,7 @@ sub runconfig { print C "$a\n"; close(C); - my $cmd="$curl -K config $url"; + my $cmd = "$curl -K config $url"; my $rc = system("$cmd >curl-output 2>&1 > 8; @@ -218,7 +218,7 @@ sub runconfig { } # run curl command lines using -K -my $end = time() + $seconds/2; +my $end = time() + $seconds / 2; my $c = 0; print "Running command lines\n"; do { @@ -228,7 +228,7 @@ sub runconfig { print "$c command lines\n"; # run curl command lines -$end = time() + $seconds/2; +$end = time() + $seconds / 2; $c = 0; print "Running config lines\n"; do { diff --git a/.github/scripts/requirements.txt b/.github/scripts/requirements.txt index 19993371414f..adf6d03ba456 100644 --- a/.github/scripts/requirements.txt +++ b/.github/scripts/requirements.txt @@ -4,6 +4,5 @@ cmakelang==0.6.13 codespell==2.4.2 -pytype==2024.10.11 reuse==6.2.0 -ruff==0.15.10 +ruff==0.15.16 diff --git a/.github/scripts/typos.toml b/.github/scripts/typos.toml index 8d8511953a81..3192a2987cf7 100644 --- a/.github/scripts/typos.toml +++ b/.github/scripts/typos.toml @@ -6,7 +6,7 @@ extend-ignore-identifiers-re = [ "^(ba|fo|pn|PN|UE)$", "^(CNA|cpy|ser)$", - "^(ECT0|ECT1|HELO|htpts|PASE)$", + "^(ECT0|ECT1|HELO|htpts|mport|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 diff --git a/.github/scripts/verify-examples.pl b/.github/scripts/verify-examples.pl index 007369b4abbc..a23dc412f08b 100755 --- a/.github/scripts/verify-examples.pl +++ b/.github/scripts/verify-examples.pl @@ -68,7 +68,7 @@ sub extract { print O "/* !checksrc! disable BANNEDFUNC all */\n"; # for fopen() print O "/* !checksrc! disable COPYRIGHT all */\n"; print O "/* !checksrc! disable UNUSEDIGNORE all */\n"; - printf O "#line %d \"$f\"\n", $iline+1; + printf O "#line %d \"$f\"\n", $iline + 1; } } elsif($syn == 2) { diff --git a/.github/stale.yml b/.github/stale.yml index 69a822e78be8..1c2b57c2cf90 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -15,7 +15,7 @@ staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you + recent activity. It is closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 1571983ced9e..83f5afe7a1aa 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -74,7 +74,7 @@ jobs: - name: 'typos' timeout-minutes: 2 run: | - HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install typos-cli + /home/linuxbrew/.linuxbrew/bin/brew install typos-cli eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" typos --version .github/scripts/typos.sh @@ -88,18 +88,34 @@ jobs: run: | scripts/perlcheck.sh - - name: 'pytype' + - name: 'ruff' run: | source ~/venv/bin/activate - find . -name '*.py' -exec pytype -j auto -k -- {} + + scripts/pythonlint.sh - - name: 'ruff' + pytype: + name: 'pytype' + runs-on: ubuntu-24.04-arm # pytype is discontinued and requires python 3.8-3.12 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - 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 pytype==2024.10.11 \ + -r .github/scripts/requirements.txt \ + -r tests/http/requirements.txt \ + -r tests/requirements.txt + + - name: 'check' run: | source ~/venv/bin/activate - scripts/pythonlint.sh + find . -name '*.py' -exec pytype -j auto -k -- {} + complexity: - name: 'complexity' + name: 'complexity and function sizes' runs-on: ubuntu-slim timeout-minutes: 3 steps: @@ -110,16 +126,18 @@ jobs: sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt sudo apt-get -o Dpkg::Use-Pty=0 update - sudo apt-get -o Dpkg::Use-Pty=0 install \ - pmccabe + sudo apt-get -o Dpkg::Use-Pty=0 install pmccabe - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: 'check scores' + - name: 'check function complexity' run: ./scripts/top-complexity + - name: 'check function lengths' + run: ./scripts/top-length + xmllint: name: 'xmllint' runs-on: ubuntu-slim @@ -131,8 +149,7 @@ jobs: sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt sudo apt-get -o Dpkg::Use-Pty=0 update - sudo apt-get -o Dpkg::Use-Pty=0 install \ - libxml2-utils + sudo apt-get -o Dpkg::Use-Pty=0 install libxml2-utils - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -148,7 +165,7 @@ jobs: steps: - name: 'install prereqs' timeout-minutes: 2 - run: HOMEBREW_NO_AUTO_UPDATE=1 /home/linuxbrew/.linuxbrew/bin/brew install actionlint shellcheck zizmor + run: /home/linuxbrew/.linuxbrew/bin/brew install actionlint shellcheck zizmor - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -161,12 +178,19 @@ jobs: eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" zizmor --persona pedantic .github/workflows/*.yml .github/dependabot.yml + - name: 'zizmor GHA (auditor, warning-only)' + env: + GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}' + run: | + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + zizmor --persona auditor .github/workflows/*.yml .github/dependabot.yml || true + - 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 + actionlint --ignore matrix --ignore ubuntu-26.04 .github/workflows/*.yml - name: 'shellcheck CI' run: | diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 8d4f6f9dbf0d..15b91c18c41a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -49,13 +49,13 @@ jobs: persist-credentials: false - name: 'initialize' - uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: actions, python queries: security-extended - name: 'perform analysis' - uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 c: if: ${{ github.repository_owner == 'curl' || github.event_name != 'schedule' }} @@ -80,7 +80,7 @@ jobs: sudo 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 + /home/linuxbrew/.linuxbrew/bin/brew install c-ares gsasl libnghttp3 libngtcp2 mbedtls rustls-ffi - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -92,7 +92,7 @@ jobs: - name: 'initialize' # https://github.com/github/codeql-action/blob/main/init/action.yml - uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 with: languages: cpp build-mode: manual @@ -128,7 +128,7 @@ jobs: 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 + -DCURL_USE_GSASL=ON -DCURL_USE_GSSAPI=ON -DUSE_SSLS_EXPORT=ON -DUSE_PROXY_HTTP3=ON cmake --build _bld2 cmake --build _bld2 --target testdeps cmake --build _bld2 --target curl-examples-build @@ -139,4 +139,4 @@ jobs: - name: 'perform analysis' # https://github.com/github/codeql-action/blob/main/analyze/action.yml - uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4 + uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index e6a417944a28..bcab34395f59 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -89,7 +89,7 @@ jobs: # shellcheck disable=SC2181 while [[ $? == 0 ]]; do for i in 1 2 3; do - if brew update && brew install automake libtool; then + if brew install automake libtool; then break 2 else echo "Error: wait to try again: $i" @@ -149,7 +149,9 @@ jobs: - name: 'install packages' timeout-minutes: 1 run: | + sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt + sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 6469448b5044..2f47321a1996 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -112,14 +112,16 @@ jobs: export CW_CONFIG='-main-werror-unitybatch-nocertdata-linux-musl-r64-x64' export CW_REVISION="${GITHUB_SHA}" . ./_versions.sh + export CW_CCSUFFIX='-19' + export CW_GCCSUFFIX='-14' sudo podman image trust set --type reject default sudo podman image trust set --type accept docker.io/library - time podman pull "${OCI_IMAGE_DEBIAN}" + 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_|DO_NOT_TRACK|GITHUB_)') \ - "${OCI_IMAGE_DEBIAN}" \ + "${OCI_IMAGE_DEBIAN_STABLE}" \ sh -c ./_ci-linux-debian.sh mac-clang: diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index bbe1e3864431..6aa969cd0f4f 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -49,7 +49,7 @@ jobs: - name: 'maketgz' run: SOURCE_DATE_EPOCH=1711526400 ./scripts/maketgz 99.98.97 - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: 'release-tgz' path: 'curl-99.98.97.tar.gz' @@ -268,7 +268,7 @@ jobs: matrix: image: [ubuntu-24.04-arm, macos-latest, windows-2022] steps: - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 if: ${{ contains(matrix.image, 'windows') }} with: msystem: mingw64 @@ -285,22 +285,24 @@ jobs: 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 pkg.bin + --location --proto-redir =https "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 find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt + sudo apt-get -o Dpkg::Use-Pty=0 update sudo 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-aarch64.tar.gz" --output pkg.bin + --location --proto-redir =https "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}-macos-universal.tar.gz" --output pkg.bin + --location --proto-redir =https "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 @@ -367,3 +369,41 @@ jobs: export TEST_CMAKE_FLAGS='-DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc -DOPENSSL_ROOT_DIR=C:/msys64/mingw64' fi ./tests/cmake/test.sh find_package ${TESTOPTS} -DCURL_USE_OPENSSL=ON + + verify-tarball-downloads: + name: 'Verify tarball downloads' + runs-on: ubuntu-slim + timeout-minutes: 2 + steps: + - name: 'download and import GPG key' + env: + CURL_GPG_ID: 27EDEAF22F3ABCEB50DB9A125CC908FDB71E12C2 + run: | + for keyserver in \ + https://keyserver.ubuntu.com/ \ + https://pgpkeys.eu/ \ + ; do + echo "--- Downloading from ${keyserver}..." + if curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + "${keyserver}pks/lookup?op=get&options=mr&exact=on&search=0x${CURL_GPG_ID}" \ + | gpg --batch --keyserver-options timeout=15 --display-charset utf-8 --keyid-format 0xlong --import --status-fd 1 2>&1; then + break + fi + done + + - name: 'download and verify tarballs' + run: | + echo "--- Detecting latest curl tarball version..." + curl_version="$(curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused https://curl.se/info.json \ + | jq --raw-output .Version)" + + for suffix in .tar.bz2 .tar.gz .tar.xz .zip; do + echo "--- Downloading ${curl_version} ${suffix}..." + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + --output pkg.bin "https://curl.se/download/curl-${curl_version}${suffix}" \ + --output pkg.sig "https://curl.se/download/curl-${curl_version}${suffix}.asc" + echo "--- Verifying ${curl_version} ${suffix}..." + gpg --batch --keyserver-options timeout=15 --display-charset utf-8 --keyid-format 0xlong --verify-options show-primary-uid-only \ + --verify pkg.sig pkg.bin 2>&1 + echo '---' + done diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index 91699e18b212..2be23997447c 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -36,67 +36,43 @@ env: CURL_CI: github CURL_TEST_MIN: 1850 DO_NOT_TRACK: '1' - # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com - OPENSSL_VERSION: 4.0.0 - # manually bumped - OPENSSL_PREV_VERSION: 3.6.2 - OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f - # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com - LIBRESSL_VERSION: 4.3.1 # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - AWSLC_VERSION: 1.71.0 + AWSLC_VERSION: 5.0.0 # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260413.0 + BORINGSSL_VERSION: 0.20260616.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=libressl/portable versioning=semver registryUrl=https://github.com + LIBRESSL_VERSION: 4.3.2 + # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com + OPENSSL_VERSION: 4.0.1 + # manually bumped + OPENSSL_PREV_VERSION: 3.6.2 + OPENSSL_PREV_SHA256: aaf51a1fe064384f811daeaeb4ec4dce7340ec8bd893027eee676af31e83a04f + # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com + QUICHE_VERSION: 0.29.2 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com WOLFSSL_VERSION: 5.9.1 # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com - NGHTTP3_VERSION: 1.15.0 + NGHTTP3_VERSION: 1.16.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com - NGTCP2_VERSION: 1.22.1 + NGTCP2_VERSION: 1.23.0 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com NGHTTP2_VERSION: 1.69.0 - # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com - QUICHE_VERSION: 0.24.7 + # no tagged releases + H2O_VERSION: 11b0cfa2771e3ccad4a852e72473e4e278ab1de7 # 2026-05-28 + H2O_SHA256: 5ae1bd7b09970d7d49c41fa68193e24da04c2a7ac5581fbe2affc79200b0721f jobs: build-cache: name: 'Build caches' - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 steps: - - name: 'cache openssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - id: cache-openssl-http3-no-deprecated - env: - cache-name: cache-openssl-http3-no-deprecated - with: - path: ~/openssl/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} - - - name: 'cache openssl-prev' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - id: cache-openssl-prev-http3-no-deprecated - env: - cache-name: cache-openssl-prev-http3-no-deprecated - with: - path: ~/openssl-prev/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} - - - name: 'cache libressl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - id: cache-libressl - env: - cache-name: cache-libressl - with: - path: ~/libressl/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - - name: 'cache awslc' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-awslc env: cache-name: cache-awslc @@ -105,7 +81,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.AWSLC_VERSION }} - name: 'cache boringssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-boringssl env: cache-name: cache-boringssl @@ -114,7 +90,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.BORINGSSL_VERSION }} - name: 'cache nettle' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nettle env: cache-name: cache-nettle @@ -123,7 +99,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NETTLE_VERSION }} - name: 'cache gnutls' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-gnutls env: cache-name: cache-gnutls @@ -131,8 +107,35 @@ jobs: path: ~/gnutls/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }}-${{ env.NETTLE_VERSION }} + - name: 'cache libressl' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-libressl + env: + cache-name: cache-libressl + with: + path: ~/libressl/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} + + - name: 'cache openssl' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-openssl-http3-no-deprecated + env: + cache-name: cache-openssl-http3-no-deprecated + with: + path: ~/openssl/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} + + - name: 'cache openssl-prev' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-openssl-prev-http3 + env: + cache-name: cache-openssl-prev-http3 + with: + path: ~/openssl-prev/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} + - name: 'cache wolfssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -141,7 +144,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'cache nghttp3' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -150,7 +153,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP3_VERSION }} - name: 'cache ngtcp2' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 @@ -159,17 +162,8 @@ jobs: 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 openssl-prev' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - id: cache-ngtcp2-openssl-prev - env: - cache-name: cache-ngtcp2-openssl-prev - with: - path: ~/ngtcp2-openssl-prev/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} - - name: 'cache ngtcp2 boringssl' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2-boringssl env: cache-name: cache-ngtcp2-boringssl @@ -177,8 +171,17 @@ jobs: path: ~/ngtcp2-boringssl/build key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.BORINGSSL_VERSION }} + - name: 'cache ngtcp2 openssl-prev' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-ngtcp2-openssl-prev + env: + cache-name: cache-ngtcp2-openssl-prev + with: + path: ~/ngtcp2-openssl-prev/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} + - name: 'cache nghttp2' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp2 env: cache-name: cache-nghttp2 @@ -187,21 +190,31 @@ jobs: key: "${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGHTTP2_VERSION }}-${{ env.OPENSSL_VERSION }}-\ ${{ env.NGTCP2_VERSION }}-${{ env.NGHTTP3_VERSION }}" + - name: 'cache h2o' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-h2o + env: + cache-name: cache-h2o + with: + path: ~/h2o/build + key: "${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.H2O_VERSION }}-${{ env.OPENSSL_PREV_VERSION }}" + - id: settings if: >- - ${{ steps.cache-openssl-http3-no-deprecated.outputs.cache-hit != 'true' || - steps.cache-openssl-prev-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-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-openssl-prev.outputs.cache-hit != 'true' || - steps.cache-ngtcp2-boringssl.outputs.cache-hit != 'true' || - steps.cache-nghttp2.outputs.cache-hit != 'true' }} + ${{ !steps.cache-awslc.outputs.cache-hit || + !steps.cache-boringssl.outputs.cache-hit || + !steps.cache-nettle.outputs.cache-hit || + !steps.cache-gnutls.outputs.cache-hit || + !steps.cache-libressl.outputs.cache-hit || + !steps.cache-openssl-http3-no-deprecated.outputs.cache-hit || + !steps.cache-openssl-prev-http3.outputs.cache-hit || + !steps.cache-wolfssl.outputs.cache-hit || + !steps.cache-nghttp3.outputs.cache-hit || + !steps.cache-ngtcp2-boringssl.outputs.cache-hit || + !steps.cache-ngtcp2-openssl-prev.outputs.cache-hit || + !steps.cache-ngtcp2.outputs.cache-hit || + !steps.cache-nghttp2.outputs.cache-hit || + !steps.cache-h2o.outputs.cache-hit }} run: echo 'needs-build=true' >> "$GITHUB_OUTPUT" @@ -216,51 +229,16 @@ jobs: libtool autoconf automake pkgconf \ libbrotli-dev libzstd-dev zlib1g-dev \ libev-dev \ + libuv1-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-no-deprecated.outputs.cache-hit != 'true' }} - run: | - cd ~ - git clone --quiet --depth 1 --branch "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl - cd openssl - ./config --prefix="$PWD"/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated - make - make -j1 install_sw - - - name: 'build openssl-prev' - if: ${{ steps.cache-openssl-prev-http3-no-deprecated.outputs.cache-hit != 'true' }} - run: | - cd ~ - curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ - --location "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_PREV_VERSION}/openssl-${OPENSSL_PREV_VERSION}.tar.gz" --output pkg.bin - sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OPENSSL_PREV_SHA256}" && tar -xzf pkg.bin && rm -f pkg.bin - cd "openssl-${OPENSSL_PREV_VERSION}" - ./config --prefix=/home/runner/openssl-prev/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated - make - make -j1 install_sw - - - name: 'build libressl' - if: ${{ steps.cache-libressl.outputs.cache-hit != 'true' }} - 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" --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 . - cmake --install . - name: 'build awslc' - if: ${{ steps.cache-awslc.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-awslc.outputs.cache-hit }} 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" --output pkg.bin + --location --proto-redir =https "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 @@ -268,7 +246,7 @@ jobs: cmake --install . - name: 'build boringssl' - if: ${{ steps.cache-boringssl.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-boringssl.outputs.cache-hit }} run: | mkdir boringssl-src cd boringssl-src @@ -280,11 +258,11 @@ jobs: cmake --install . - name: 'build nettle' - if: ${{ steps.cache-nettle.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-nettle.outputs.cache-hit }} run: | cd ~ 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 + --location --proto-redir =https "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 @@ -293,7 +271,7 @@ jobs: make install - name: 'build gnutls' - if: ${{ steps.cache-gnutls.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-gnutls.outputs.cache-hit }} run: | cd ~ curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ @@ -309,8 +287,42 @@ jobs: --disable-guile --disable-doc --disable-tests --disable-tools make install + - name: 'build libressl' + if: ${{ !steps.cache-libressl.outputs.cache-hit }} + run: | + cd ~ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/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 . + cmake --install . + + - name: 'build openssl' + if: ${{ !steps.cache-openssl-http3-no-deprecated.outputs.cache-hit }} + run: | + cd ~ + git clone --quiet --depth 1 --branch "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl + cd openssl + ./config --prefix="$PWD"/build --libdir=lib no-makedepend no-apps no-docs no-tests no-deprecated + make + make -j1 install_sw + + - name: 'build openssl-prev' + if: ${{ !steps.cache-openssl-prev-http3.outputs.cache-hit }} + run: | + cd ~ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location --proto-redir =https "https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_PREV_VERSION}/openssl-${OPENSSL_PREV_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${OPENSSL_PREV_SHA256}" && tar -xzf pkg.bin && rm -f pkg.bin + cd "openssl-${OPENSSL_PREV_VERSION}" + ./config --prefix=/home/runner/openssl-prev/build --libdir=lib no-makedepend no-apps no-docs no-tests + make + make -j1 install_sw + - name: 'build wolfssl' - if: ${{ steps.cache-wolfssl.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-wolfssl.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${WOLFSSL_VERSION}-stable" https://github.com/wolfSSL/wolfssl @@ -322,7 +334,7 @@ jobs: make install - name: 'build nghttp3' - if: ${{ steps.cache-nghttp3.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-nghttp3.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${NGHTTP3_VERSION}" https://github.com/ngtcp2/nghttp3 @@ -334,7 +346,7 @@ jobs: make install - name: 'build ngtcp2' - if: ${{ steps.cache-ngtcp2.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-ngtcp2.outputs.cache-hit }} # building twice to get crypto libs for ossl, libressl and awslc installed run: | cd ~ @@ -357,7 +369,7 @@ jobs: make install - name: 'build ngtcp2 openssl-prev' - if: ${{ steps.cache-ngtcp2-openssl-prev.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-ngtcp2-openssl-prev.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 ngtcp2-openssl-prev @@ -369,7 +381,7 @@ jobs: make install - name: 'build ngtcp2 boringssl' - if: ${{ steps.cache-ngtcp2-boringssl.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-ngtcp2-boringssl.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${NGTCP2_VERSION}" https://github.com/ngtcp2/ngtcp2 ngtcp2-boringssl @@ -382,7 +394,7 @@ jobs: make install - name: 'build nghttp2' - if: ${{ steps.cache-nghttp2.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-nghttp2.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "v${NGHTTP2_VERSION}" https://github.com/nghttp2/nghttp2 @@ -399,10 +411,22 @@ jobs: --with-libbrotlienc --with-libbrotlidec make install + - name: 'build h2o' + if: ${{ !steps.cache-h2o.outputs.cache-hit }} + run: | + cd ~ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 6 --retry-connrefused \ + --location --proto-redir =https "https://github.com/h2o/h2o/archive/${H2O_VERSION}.tar.gz" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${H2O_SHA256}" && tar -xzf pkg.bin && rm -f pkg.bin + cd "h2o-${H2O_VERSION}" + cmake -B . -G Ninja -DWITHOUT_LIBS=ON -DOPENSSL_ROOT_DIR=/home/runner/openssl-prev/build -DCMAKE_INSTALL_PREFIX=/home/runner/h2o/build + cmake --build . + cmake --install . + linux: name: ${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.build.name }} needs: build-cache - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 10 env: CURL_TRACE_PKG_CONFIG: '1' @@ -412,57 +436,6 @@ jobs: fail-fast: false matrix: build: - - name: 'openssl' - tflags: '--min=1700' - 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: >- - --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --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_LDAP=ON - -DUSE_ECH=ON - -DCMAKE_UNITY_BUILD=ON - - - name: 'openssl-prev' - install_steps: skipall - LDFLAGS: -Wl,-rpath,/home/runner/openssl-prev/build/lib - PKG_CONFIG_PATH: "\ - /home/runner/openssl-prev/build/lib/pkgconfig:\ - /home/runner/nghttp3/build/lib/pkgconfig:\ - /home/runner/nghttp2-openssl-prev/build/lib/pkgconfig" - configure: >- - --with-openssl=/home/runner/openssl-prev/build --with-ngtcp2=/home/runner/ngtcp2-openssl-prev/build --enable-ssls-export - - - name: 'openssl-prev' - tflags: '--min=1700' - PKG_CONFIG_PATH: "\ - /home/runner/openssl-prev/build/lib/pkgconfig:\ - /home/runner/nghttp3/build/lib/pkgconfig:\ - /home/runner/ngtcp2-openssl-prev/build/lib/pkgconfig:\ - /home/runner/nghttp2/build/lib/pkgconfig" - generate: >- - -DOPENSSL_ROOT_DIR=/home/runner/openssl-prev/build -DUSE_NGTCP2=ON - -DCURL_DISABLE_LDAP=ON - - - name: 'libressl' - install_steps: skipall - 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: >- - --with-openssl=/home/runner/libressl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ssls-export - --enable-unity - - - name: 'libressl' - 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 - - name: 'awslc' install_steps: skipall LDFLAGS: -Wl,-rpath,/home/runner/awslc/build/lib @@ -515,29 +488,63 @@ jobs: -DCURL_USE_GNUTLS=ON -DUSE_NGTCP2=ON -DCURL_USE_LIBSSH=ON -DCMAKE_UNITY_BUILD=ON - - name: 'wolfssl' - install_packages: libssh2-1-dev + - name: 'libressl' install_steps: skipall - 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 + 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: >- - --with-wolfssl=/home/runner/wolfssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --with-libssh2 --enable-ssls-export + --with-openssl=/home/runner/libressl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ssls-export --enable-unity - - name: 'wolfssl' - install_packages: libssh2-1-dev - tflags: '--min=1900' - 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 + - name: 'libressl' + 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: >- - -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON - -DUSE_ECH=ON + -DOPENSSL_ROOT_DIR=/home/runner/libressl/build -DUSE_NGTCP2=ON + + - name: 'openssl' + tflags: '--min=1700' + 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: >- + --with-openssl=/home/runner/openssl/build --with-ngtcp2=/home/runner/ngtcp2/build --enable-ech --enable-ssls-export --enable-proxy-http3 + + - 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_LDAP=ON + -DUSE_ECH=ON -DUSE_PROXY_HTTP3=ON + -DCMAKE_UNITY_BUILD=ON + + - name: 'openssl-prev' + install_steps: skipall + LDFLAGS: -Wl,-rpath,/home/runner/openssl-prev/build/lib + PKG_CONFIG_PATH: "\ + /home/runner/openssl-prev/build/lib/pkgconfig:\ + /home/runner/nghttp3/build/lib/pkgconfig:\ + /home/runner/nghttp2-openssl-prev/build/lib/pkgconfig" + configure: >- + --with-openssl=/home/runner/openssl-prev/build --with-ngtcp2=/home/runner/ngtcp2-openssl-prev/build --enable-ssls-export + + - name: 'openssl-prev' + tflags: '--min=1700' + PKG_CONFIG_PATH: "\ + /home/runner/openssl-prev/build/lib/pkgconfig:\ + /home/runner/nghttp3/build/lib/pkgconfig:\ + /home/runner/ngtcp2-openssl-prev/build/lib/pkgconfig:\ + /home/runner/nghttp2/build/lib/pkgconfig" + generate: >- + -DOPENSSL_ROOT_DIR=/home/runner/openssl-prev/build -DUSE_NGTCP2=ON + -DCURL_DISABLE_LDAP=ON - name: 'quiche' install_steps: skipall LDFLAGS: -Wl,-rpath,/home/runner/quiche/target/release PKG_CONFIG_PATH: /home/runner/nghttp2/build/lib/pkgconfig configure: >- - --with-openssl=/home/runner/quiche/quiche/deps/boringssl/src + --with-openssl=/home/runner/quiche/boringssl --with-quiche=/home/runner/quiche/target/release --with-ca-fallback --enable-unity @@ -545,10 +552,27 @@ jobs: - name: 'quiche' 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 + -DOPENSSL_ROOT_DIR=/home/runner/quiche/boringssl -DUSE_QUICHE=ON -DCURL_CA_FALLBACK=ON + - name: 'wolfssl' + install_packages: libssh2-1-dev + install_steps: skipall + 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: >- + --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=1900' + 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 + steps: - name: 'install prereqs' timeout-minutes: 2 @@ -567,44 +591,10 @@ jobs: 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' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - id: cache-openssl-http3-no-deprecated - env: - 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 openssl-prev' - if: ${{ contains(matrix.build.name, 'openssl-prev') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - id: cache-openssl-prev-http3-no-deprecated - env: - cache-name: cache-openssl-prev-http3-no-deprecated - with: - path: ~/openssl-prev/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} - fail-on-cache-miss: true - - - name: 'cache libressl' - if: ${{ contains(matrix.build.name, 'libressl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - id: cache-libressl - env: - cache-name: cache-libressl - with: - path: ~/libressl/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - fail-on-cache-miss: true - name: 'cache awslc' if: ${{ contains(matrix.build.name, 'awslc') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-awslc env: cache-name: cache-awslc @@ -615,7 +605,7 @@ jobs: - name: 'cache boringssl' if: ${{ contains(matrix.build.name, 'boringssl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-boringssl env: cache-name: cache-boringssl @@ -626,7 +616,7 @@ jobs: - name: 'cache nettle' if: ${{ contains(matrix.build.name, 'gnutls') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nettle env: cache-name: cache-nettle @@ -637,7 +627,7 @@ jobs: - name: 'cache gnutls' if: ${{ contains(matrix.build.name, 'gnutls') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-gnutls env: cache-name: cache-gnutls @@ -646,9 +636,41 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.GNUTLS_VERSION }}-${{ env.NETTLE_VERSION }} fail-on-cache-miss: true + - name: 'cache libressl' + if: ${{ contains(matrix.build.name, 'libressl') }} + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-libressl + env: + cache-name: cache-libressl + with: + path: ~/libressl/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} + fail-on-cache-miss: true + + - name: 'cache openssl' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-openssl-http3-no-deprecated + env: + 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 openssl-prev' + if: ${{ contains(matrix.build.name, 'openssl-prev') }} + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-openssl-prev-http3 + env: + cache-name: cache-openssl-prev-http3 + with: + path: ~/openssl-prev/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.OPENSSL_PREV_VERSION }} + fail-on-cache-miss: true + - name: 'cache wolfssl' if: ${{ contains(matrix.build.name, 'wolfssl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -658,7 +680,7 @@ jobs: fail-on-cache-miss: true - name: 'cache nghttp3' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -668,7 +690,7 @@ jobs: fail-on-cache-miss: true - name: 'cache ngtcp2' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 @@ -678,20 +700,9 @@ jobs: ${{ env.LIBRESSL_VERSION }}-${{ env.AWSLC_VERSION }}-${{ env.NETTLE_VERSION }}-${{ env.GNUTLS_VERSION }}-${{ env.WOLFSSL_VERSION }}" fail-on-cache-miss: true - - name: 'cache ngtcp2 openssl-prev' - if: ${{ contains(matrix.build.name, 'openssl-prev') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 - id: cache-ngtcp2-openssl-prev - env: - cache-name: cache-ngtcp2-openssl-prev - with: - path: ~/ngtcp2-openssl-prev/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} - fail-on-cache-miss: true - - name: 'cache ngtcp2 boringssl' if: ${{ contains(matrix.build.name, 'boringssl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-ngtcp2-boringssl env: cache-name: cache-ngtcp2-boringssl @@ -700,8 +711,19 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.BORINGSSL_VERSION }} fail-on-cache-miss: true + - name: 'cache ngtcp2 openssl-prev' + if: ${{ contains(matrix.build.name, 'openssl-prev') }} + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-ngtcp2-openssl-prev + env: + cache-name: cache-ngtcp2-openssl-prev + with: + path: ~/ngtcp2-openssl-prev/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.NGTCP2_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} + fail-on-cache-miss: true + - name: 'cache nghttp2' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp2 env: cache-name: cache-nghttp2 @@ -710,9 +732,19 @@ jobs: 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 h2o' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-h2o + env: + cache-name: cache-h2o + with: + path: ~/h2o/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.H2O_VERSION }}-${{ env.OPENSSL_PREV_VERSION }} + fail-on-cache-miss: true + - name: 'cache quiche' if: ${{ contains(matrix.build.name, 'quiche') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-quiche env: cache-name: cache-quiche @@ -721,24 +753,22 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.QUICHE_VERSION }} - name: 'build quiche and boringssl' - if: ${{ contains(matrix.build.name, 'quiche') && steps.cache-quiche.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.name, 'quiche') && !steps.cache-quiche.outputs.cache-hit }} run: | cd ~ git clone --quiet --depth 1 --branch "${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 #### - sed -i -e 's/cmake = "0.1"/cmake = "=0.1.45"/' quiche/Cargo.toml - 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 - find target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' quiche/deps/boringssl/src/lib \; + cd .. + mkdir -p quiche/boringssl/lib + find quiche/target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' quiche/boringssl/lib \; + find quiche/target/release/build/boring-sys-*/out/boringssl/src -maxdepth 1 \( -name include \) -exec ln -vsf -- '../../{}' quiche/boringssl \; # include dir - # /home/runner/quiche/quiche/deps/boringssl/src/include + # /home/runner/quiche/boringssl/include # lib dir - # /home/runner/quiche/quiche/deps/boringssl/src/lib + # /home/runner/quiche/boringssl/lib - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: @@ -759,10 +789,11 @@ jobs: 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 \ + -DCMAKE_C_COMPILER_TARGET="$(uname -m)-unknown-linux-gnu" -DBUILD_STATIC_LIBS=ON \ -DCURL_WERROR=ON -DENABLE_DEBUG=ON \ -DCURL_USE_LIBUV=ON -DCURL_ENABLE_NTLM=ON \ -DTEST_NGHTTPX=/home/runner/nghttp2/build/bin/nghttpx \ + -DH2O=/home/runner/h2o/build/bin/h2o \ -DHTTPD_NGHTTPX=/home/runner/nghttp2/build/bin/nghttpx \ ${MATRIX_GENERATE} ${options} else @@ -770,6 +801,7 @@ jobs: 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-h2o=/home/runner/h2o/build/bin/h2o \ --with-test-nghttpx=/home/runner/nghttp2/build/bin/nghttpx \ ${MATRIX_CONFIGURE} fi diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index e35f09304582..3922b8b7dfc0 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -2,12 +2,11 @@ # # SPDX-License-Identifier: curl -# This workflow will triage pull requests and apply a label based on the +# This workflow triages pull requests and applies a label based on the # paths that are modified in the pull request. # -# To use this workflow, you will need to set up a .github/labeler.yml -# file with configuration. For more information, see: -# https://github.com/actions/labeler +# To use this workflow, you need to set up a .github/labeler.yml file with +# configuration. For more information, see: https://github.com/actions/labeler name: 'Labeler' @@ -31,6 +30,6 @@ jobs: pull-requests: write # To edit labels on PRs steps: - - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 + - uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0 with: repo-token: '${{ secrets.GITHUB_TOKEN }}' diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index 11644174623b..46fa9acffde5 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -56,7 +56,7 @@ jobs: cmake-autotools: name: 'autotools & cmake' runs-on: ubuntu-latest - container: 'debian:stretch' + container: debian:stretch-20220622-slim@sha256:c5cd3ffceeb25b683bf5111ea89bf8049a177e00fb237235d48076a19cc80097 steps: - name: 'install prereqs' @@ -116,10 +116,8 @@ jobs: 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 - # when this job can get c-ares 1.16.0 or later, we can enable that - # again + # when this job can get libssh 0.9.0 or greater, this should get that enabled again + # when this job can get c-ares 1.16.0 or greater, this should get that enabled again - name: 'CM configure (out-of-tree, zstd, gssapi)' run: | diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d9e09b518782..fff7f1c8765c 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -34,29 +34,29 @@ env: CURL_CI: github CURL_TEST_MIN: 1660 DO_NOT_TRACK: '1' + # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com + AWSLC_VERSION: 5.0.0 + # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com + BORINGSSL_VERSION: 0.20260616.0 + # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com + FIL_C_VERSION: '0.680' # renovate: datasource=github-tags depName=libressl/portable versioning=semver registryUrl=https://github.com - LIBRESSL_VERSION: 4.3.1 - # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSL_VERSION: 5.9.1 + LIBRESSL_VERSION: 4.3.2 # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com 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.71.0 - # renovate: datasource=github-tags depName=google/boringssl versioning=semver registryUrl=https://github.com - BORINGSSL_VERSION: 0.20260413.0 + # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com + NGHTTP2_VERSION: 1.69.0 + # handled in renovate.json + OPENLDAP_VERSION: 2.6.10 # renovate: datasource=github-releases depName=openssl/openssl versioning=semver extractVersion=^openssl-(?.+)$ registryUrl=https://github.com - OPENSSL_VERSION: 4.0.0 + OPENSSL_VERSION: 4.0.1 # renovate: datasource=github-tags depName=rustls/rustls-ffi versioning=semver registryUrl=https://github.com RUSTLS_VERSION: 0.15.3 - # 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.69.0 - # renovate: datasource=github-releases depName=pizlonator/fil-c versioning=semver-coerced registryUrl=https://github.com - FIL_C_VERSION: 0.678 + # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com + WOLFSSL_VERSION: 5.9.1 jobs: linux: @@ -72,10 +72,24 @@ jobs: fail-fast: false matrix: build: + - name: 'awslc' + install_steps: awslc pytest + LDFLAGS: -Wl,-rpath,/home/runner/awslc/lib + configure: --with-openssl=/home/runner/awslc --enable-ech --enable-ntlm --enable-smb + + - name: 'awslc' + install_packages: libidn2-dev + install_steps: awslc + 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_steps: boringssl pytest + generate: -DOPENSSL_ROOT_DIR=/home/runner/boringssl -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON + - 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 + install_steps: libressl-c-arm pytest codeset-test1 LDFLAGS: -Wl,-rpath,/home/runner/libressl/lib configure: --with-openssl=/home/runner/libressl --with-gssapi --enable-debug @@ -101,27 +115,17 @@ jobs: LDFLAGS: -Wl,-rpath,/home/runner/libressl/lib configure: --with-openssl=/home/runner/libressl --enable-debug - - name: 'wolfssl-all' - 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 1' - image: ubuntu-24.04-arm - install_packages: valgrind - install_steps: wolfssl-opensslextra-arm - tflags: '--min=815 1 to 1000' - LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib - configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug - - - name: 'wolfssl-opensslextra valgrind 2' - image: ubuntu-24.04-arm - install_packages: valgrind - install_steps: wolfssl-opensslextra-arm - tflags: '--min=835 1001 to 9999' - LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib - configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug + - 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: 'mbedtls gss valgrind 1' image: ubuntu-24.04-arm @@ -167,33 +171,60 @@ jobs: -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF -DCURL_COMPLETION_FISH=ON -DCURL_COMPLETION_ZSH=ON - - name: 'awslc' - install_steps: awslc pytest - LDFLAGS: -Wl,-rpath,/home/runner/awslc/lib - configure: --with-openssl=/home/runner/awslc --enable-ech --enable-ntlm + - name: 'rustls valgrind 1' + install_packages: libnghttp2-dev libldap-dev valgrind + install_steps: rust rustls + tflags: '--min=820 1 to 1000' + generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - - name: 'awslc' - install_packages: libidn2-dev - install_steps: awslc - 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: 'rustls valgrind 2' + install_packages: libnghttp2-dev libldap-dev valgrind + install_steps: rust rustls + tflags: '--min=830 1001 to 9999' + generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - - name: 'boringssl' - install_steps: boringssl pytest - generate: -DOPENSSL_ROOT_DIR=/home/runner/boringssl -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON + - name: 'rustls' + install_packages: libnghttp2-dev libldap-dev + install_steps: rust rustls skiprun pytest + configure: --with-rustls --enable-ech --enable-debug + + - name: 'wolfssl-all' + 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 1' + image: ubuntu-24.04-arm + install_packages: valgrind + install_steps: wolfssl-opensslextra-arm + tflags: '--min=815 1 to 1000' + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib + configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug + + - name: 'wolfssl-opensslextra valgrind 2' + image: ubuntu-24.04-arm + install_packages: valgrind + install_steps: wolfssl-opensslextra-arm + tflags: '--min=835 1001 to 9999' + LDFLAGS: -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib + configure: --with-wolfssl=/home/runner/wolfssl-opensslextra --enable-ech --enable-debug - name: 'openssl default' install_steps: pytest configure: --with-openssl --enable-debug --disable-unity - 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 + image: ubuntu-26.04-arm + install_packages: gcc-16 libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + CC: gcc-16 tflags: '--min=965 1 to 1000' generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_GCC_ANALYZER=ON -DCURL_ENABLE_NTLM=ON - name: 'openssl libssh2 sync-resolver valgrind 2' - image: ubuntu-24.04-arm - install_packages: libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + image: ubuntu-26.04-arm + install_packages: gcc-16 libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev valgrind + CC: gcc-16 tflags: '--min=920 1001 to 9999' generate: -DENABLE_DEBUG=ON -DENABLE_THREADED_RESOLVER=OFF -DCURL_ENABLE_NTLM=ON @@ -237,7 +268,7 @@ jobs: - name: 'openssl !ipv6 !--libcurl !--digest-auth' image: ubuntu-24.04-arm - configure: --with-openssl --disable-ipv6 --enable-debug --disable-unity --disable-libcurl-option --disable-digest-auth --enable-ntlm + configure: --with-openssl --disable-ipv6 --enable-debug --disable-unity --disable-libcurl-option --disable-digest-auth --enable-ntlm --enable-smb - name: 'curl_global_init_mem debug valgrind' image: ubuntu-24.04-arm @@ -246,7 +277,7 @@ jobs: configure: >- --enable-init-mem-debug --with-openssl --disable-debug --enable-unity - --enable-ntlm + --enable-ntlm --enable-smb - name: 'openssl https-only' image: ubuntu-24.04-arm @@ -287,23 +318,13 @@ jobs: tflags: '--min=500' configure: --without-ssl --enable-debug --disable-http --disable-smtp --disable-imap --disable-unity - - 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: 'clang-tidy' - install_packages: clang-20 clang-tidy-20 libssl-dev libidn2-dev libssh2-1-dev libnghttp2-dev libldap-dev libkrb5-dev libgnutls28-dev + image: ubuntu-26.04 + install_packages: clang-22 clang-tidy-22 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: openssl@4 gsasl - CC: clang-20 + CC: clang-22 + CFLAGS: -Wunused-macros LDFLAGS: >- -Wl,-rpath,/home/runner/wolfssl-opensslextra/lib -Wl,-rpath,/home/runner/mbedtls/lib @@ -321,13 +342,15 @@ jobs: -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 + -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-22 - name: 'clang-tidy H3 c-ares !examples' - install_packages: clang-20 clang-tidy-20 libidn2-dev libssh-dev libnghttp2-dev + image: ubuntu-26.04 + install_packages: clang-22 clang-tidy-22 libidn2-dev libssh-dev libnghttp2-dev install_steps: skiprun install_steps_brew: libngtcp2 libnghttp3 c-ares - CC: clang-20 + CC: clang-22 + CFLAGS: -Wunused-macros LDFLAGS: >- -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/openssl/lib -Wl,-rpath,/home/linuxbrew/.linuxbrew/opt/libngtcp2/lib @@ -340,14 +363,16 @@ jobs: /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_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DUSE_HTTPSRR=ON -DENABLE_ARES=ON -DUSE_PROXY_HTTP3=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON - -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20 + -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-22 - name: 'address-sanitizer' - install_packages: clang-20 libssl-dev libssh-dev libidn2-dev libnghttp2-dev libubsan1 libasan8 libtsan2 + image: ubuntu-26.04 + install_packages: clang-22 libssh-dev libidn2-dev libnghttp2-dev libubsan1 libasan8 libtsan2 install_steps: pytest randcurl - CC: clang-20 + install_steps_brew: openssl@4 + CC: clang-22 CFLAGS: >- -fsanitize=address,bounds,leak,signed-integer-overflow,undefined -fno-sanitize-recover=address,bounds,leak,signed-integer-overflow,undefined @@ -357,13 +382,14 @@ jobs: -fsanitize=address,bounds,leak,signed-integer-overflow,undefined -fno-sanitize-recover=address,bounds,leak,signed-integer-overflow,undefined -ldl -lubsan - generate: -DENABLE_DEBUG=ON -DCURL_USE_LIBSSH=ON + generate: -DENABLE_DEBUG=ON -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/home/linuxbrew/.linuxbrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_USE_LIBSSH=ON - name: 'address-sanitizer H3 c-ares' - install_packages: clang-20 libubsan1 libasan8 libtsan2 + image: ubuntu-26.04 + install_packages: clang-22 libubsan1 libasan8 libtsan2 install_steps: pytest - install_steps_brew: openssl@4 libssh2 libngtcp2 libnghttp3 c-ares - CC: clang-20 + install_steps_brew: openssl libssh2 libngtcp2 libnghttp3 c-ares + CC: clang-22 CFLAGS: >- -fsanitize=address,bounds,leak,signed-integer-overflow,undefined -fno-sanitize-recover=address,bounds,leak,signed-integer-overflow,undefined @@ -379,21 +405,23 @@ jobs: /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@4 -DUSE_ECH=ON -DUSE_NGTCP2=ON - -DUSE_SSLS_EXPORT=ON -DENABLE_ARES=ON + -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 -DUSE_PROXY_HTTP3=ON - name: 'thread-sanitizer' - install_packages: clang-20 libtsan2 + image: ubuntu-26.04 + install_packages: clang-22 libtsan2 install_steps: pytest openssl-tsan - CC: clang-20 + CC: clang-22 CFLAGS: -fsanitize=thread -g LDFLAGS: -fsanitize=thread generate: -DOPENSSL_ROOT_DIR=/home/runner/openssl -DUSE_ECH=ON -DENABLE_DEBUG=ON - name: 'memory-sanitizer' - install_packages: clang-20 + image: ubuntu-26.04 + install_packages: clang-22 install_steps: randcurl - CC: clang-20 + CC: clang-22 CFLAGS: -fsanitize=memory -Wformat -Werror=format-security -Werror=array-bounds -g LDFLAGS: -fsanitize=memory LIBS: -ldl @@ -402,7 +430,7 @@ jobs: - name: 'event-based' install_packages: libssh-dev - configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl --enable-ntlm + configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl --enable-ntlm --enable-smb tflags: '-n --test-event --min=1420' - name: 'duphandle' @@ -411,23 +439,6 @@ jobs: configure: --enable-debug --enable-static --disable-shared --disable-threaded-resolver --with-libssh --with-openssl tflags: '-n --test-duphandle' - - name: 'rustls valgrind 1' - install_packages: libnghttp2-dev libldap-dev valgrind - install_steps: rust rustls - tflags: '--min=820 1 to 1000' - 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=830 1001 to 9999' - generate: -DCURL_USE_RUSTLS=ON -DUSE_ECH=ON -DENABLE_DEBUG=ON - - - name: 'rustls' - install_packages: libnghttp2-dev libldap-dev - install_steps: rust rustls skiprun pytest - configure: --with-rustls --enable-ech --enable-debug - - name: 'IntelC openssl' install_packages: libssl-dev install_steps: intelc @@ -439,20 +450,20 @@ jobs: # https://ftpmirror.infania.net/slackware/slackware64-current/source/n/curl/curl.SlackBuild 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' + container: andy5995/slackware-build-essential:15.0@sha256:f4f2242999038a2c2deb4e5727187caaae92502a7daf8353068932621e1ec92f - name: 'Alpine MUSL https-rr' configure: --enable-debug --with-ssl --with-libssh2 --with-libidn2 --with-gssapi --enable-ldap --with-libpsl --enable-httpsrr --enable-ares --enable-threaded-resolver - container: 'alpine:3.20' + container: alpine:3.23@sha256:5b10f432ef3da1b8d4c7eb6c487f2f5a8f096bc91145e68878dd4a5019afde11 # 3.23.4 - name: 'Alpine MUSL https-rr c-ares' configure: --enable-debug --with-ssl --with-libssh2 --with-libidn2 --with-gssapi --enable-ldap --with-libpsl --enable-httpsrr --enable-ares --disable-threaded-resolver - container: 'alpine:3.20' + container: alpine:3.20@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc # 3.20.10 steps: - name: 'install prereqs' if: ${{ matrix.build.container == null && !contains(matrix.build.name, 'i686') }} - timeout-minutes: 2 + timeout-minutes: 3 env: INSTALL_PACKAGES_BREW: '${{ matrix.build.install_steps_brew }}' INSTALL_PACKAGES: >- @@ -470,13 +481,7 @@ jobs: ${INSTALL_PACKAGES} \ ${MATRIX_INSTALL_PACKAGES} 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 + /home/linuxbrew/.linuxbrew/bin/brew install ${INSTALL_PACKAGES_BREW} fi - name: 'install prereqs (i686)' @@ -506,14 +511,14 @@ jobs: 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 + --location --proto-redir =https "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 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-libressl-c-arm env: cache-name: cache-libressl-c-arm @@ -522,10 +527,10 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - name: 'build libressl (c-arm)' - if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') && steps.cache-libressl-c-arm.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'libressl-c-arm') && !steps.cache-libressl-c-arm.outputs.cache-hit }} 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 + "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/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 -DCURL_ENABLE_NTLM=ON @@ -534,7 +539,7 @@ jobs: - name: 'cache libressl (filc)' if: ${{ contains(matrix.build.install_steps, 'libressl-filc') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-libressl-filc env: cache-name: cache-libressl-filc @@ -543,10 +548,10 @@ jobs: 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' }} + if: ${{ contains(matrix.build.install_steps, 'libressl-filc') && !steps.cache-libressl-filc.outputs.cache-hit }} 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 + "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/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 \ @@ -556,7 +561,7 @@ jobs: - name: 'cache nghttp2 (filc)' if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-nghttp2-filc env: cache-name: cache-nghttp2-filc @@ -565,10 +570,10 @@ jobs: 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' }} + if: ${{ contains(matrix.build.install_steps, 'nghttp2-filc') && !steps.cache-nghttp2-filc.outputs.cache-hit }} 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 + --location --proto-redir =https "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 \ @@ -579,7 +584,7 @@ jobs: - name: 'cache wolfssl (all-arm)' if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl-all-arm env: cache-name: cache-wolfssl-all-arm @@ -588,20 +593,21 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - 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' }} + if: ${{ contains(matrix.build.install_steps, 'wolfssl-all-arm') && !steps.cache-wolfssl-all-arm.outputs.cache-hit }} 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" --output pkg.bin + --location --proto-redir =https "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 --prefix=/home/runner/wolfssl-all --enable-tls13 --enable-harden --enable-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-intel)' # does support `OPENSSL_COEXIST` if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl-opensslextra-intel env: cache-name: cache-wolfssl-opensslextra-intel @@ -610,20 +616,21 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'build wolfssl (opensslextra-intel)' - if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') && steps.cache-wolfssl-opensslextra-intel.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-intel') && !steps.cache-wolfssl-opensslextra-intel.outputs.cache-hit }} 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" --output pkg.bin + --location --proto-redir =https "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 --prefix=/home/runner/wolfssl-opensslextra --enable-tls13 --enable-harden --enable-ech --enable-opensslextra \ + ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra \ + --enable-tls13 --enable-harden --enable-ech --enable-ed25519 --enable-opensslextra \ --disable-benchmark --disable-crypttests --disable-examples make install - 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 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-wolfssl-opensslextra-arm env: cache-name: cache-wolfssl-opensslextra-arm @@ -632,20 +639,21 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.WOLFSSL_VERSION }} - name: 'build wolfssl (opensslextra-arm)' - if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') && steps.cache-wolfssl-opensslextra-arm.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'wolfssl-opensslextra-arm') && !steps.cache-wolfssl-opensslextra-arm.outputs.cache-hit }} 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" --output pkg.bin + --location --proto-redir =https "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 --prefix=/home/runner/wolfssl-opensslextra --enable-tls13 --enable-harden --enable-ech --enable-opensslextra \ + ./configure --disable-dependency-tracking --prefix=/home/runner/wolfssl-opensslextra \ + --enable-tls13 --enable-harden --enable-ech --enable-ed25519 --enable-opensslextra \ --disable-benchmark --disable-crypttests --disable-examples make install - name: 'cache mbedtls (latest-intel)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-mbedtls-latest-intel env: cache-name: cache-mbedtls-latest-intel @@ -654,10 +662,10 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.MBEDTLS_VERSION }} - name: 'build mbedtls (latest-intel)' - if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') && steps.cache-mbedtls-latest-intel.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-intel') && !steps.cache-mbedtls-latest-intel.outputs.cache-hit }} 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 + --location --proto-redir =https "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 @@ -669,7 +677,7 @@ jobs: - name: 'cache mbedtls (latest-arm)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-mbedtls-latest-arm env: cache-name: cache-mbedtls-latest-arm @@ -678,10 +686,10 @@ jobs: 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' }} + if: ${{ contains(matrix.build.install_steps, 'mbedtls-latest-arm') && !steps.cache-mbedtls-latest-arm.outputs.cache-hit }} 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 + --location --proto-redir =https "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 @@ -693,7 +701,7 @@ jobs: - name: 'cache mbedtls (prev)' if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-mbedtls-prev env: cache-name: cache-mbedtls-prev @@ -702,10 +710,10 @@ jobs: 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' }} + if: ${{ contains(matrix.build.install_steps, 'mbedtls-prev') && !steps.cache-mbedtls-prev.outputs.cache-hit }} 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 + --location --proto-redir =https "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 @@ -717,7 +725,7 @@ jobs: - name: 'cache openldap (static)' if: ${{ contains(matrix.build.install_steps, 'openldap-static') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-openldap-static env: cache-name: cache-openldap-static @@ -726,10 +734,10 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.OPENLDAP_VERSION }} - name: 'build openldap (static)' - if: ${{ contains(matrix.build.install_steps, 'openldap-static') && steps.cache-openldap-static.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'openldap-static') && !steps.cache-openldap-static.outputs.cache-hit }} 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" --output pkg.bin + "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}" autoreconf -fi @@ -738,7 +746,7 @@ jobs: - name: 'cache openssl (thread sanitizer)' if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-openssl-tsan env: cache-name: cache-openssl-tsan @@ -747,7 +755,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.OPENSSL_VERSION }} - name: 'build openssl (thread sanitizer)' - if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') && steps.cache-openssl-tsan.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'openssl-tsan') && !steps.cache-openssl-tsan.outputs.cache-hit }} run: | git clone --quiet --depth 1 --branch "openssl-${OPENSSL_VERSION}" https://github.com/openssl/openssl cd openssl @@ -757,7 +765,7 @@ jobs: - name: 'cache awslc' if: ${{ contains(matrix.build.install_steps, 'awslc') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-awslc env: cache-name: cache-awslc @@ -766,10 +774,10 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.AWSLC_VERSION }} - name: 'build awslc' - if: ${{ contains(matrix.build.install_steps, 'awslc') && steps.cache-awslc.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'awslc') && !steps.cache-awslc.outputs.cache-hit }} 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" --output pkg.bin + --location --proto-redir =https "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 @@ -778,7 +786,7 @@ jobs: - name: 'cache boringssl' if: ${{ contains(matrix.build.install_steps, 'boringssl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-boringssl env: cache-name: cache-boringssl @@ -787,7 +795,7 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.BORINGSSL_VERSION }} - name: 'build boringssl' - if: ${{ contains(matrix.build.install_steps, 'boringssl') && steps.cache-boringssl.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'boringssl') && !steps.cache-boringssl.outputs.cache-hit }} run: | mkdir boringssl-src cd boringssl-src @@ -800,7 +808,7 @@ jobs: - name: 'cache rustls' if: ${{ contains(matrix.build.install_steps, 'rustls') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-rustls env: cache-name: cache-rustls @@ -809,11 +817,11 @@ jobs: key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.RUSTLS_VERSION }} - name: 'fetch rustls deb' - if: ${{ contains(matrix.build.install_steps, 'rustls') && steps.cache-rustls.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'rustls') && !steps.cache-rustls.outputs.cache-hit }} run: | 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 pkg.bin + --location --proto-redir =https "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' @@ -823,11 +831,12 @@ jobs: - name: 'install Intel compilers' if: ${{ contains(matrix.build.install_steps, 'intelc') }} - timeout-minutes: 2 + timeout-minutes: 4 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 find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt 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 @@ -857,7 +866,7 @@ jobs: if [ "${MATRIX_BUILD}" = 'cmake' ]; then cmake -B bld -G Ninja \ -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_C_COMPILER_TARGET="$(uname -m)-pc-linux-gnu" -DBUILD_STATIC_LIBS=ON \ + -DCMAKE_C_COMPILER_TARGET="$(uname -m)-unknown-linux-gnu" -DBUILD_STATIC_LIBS=ON \ -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON \ ${MATRIX_GENERATE} else @@ -946,7 +955,7 @@ jobs: TFLAGS+=' --buildinfo' # only test-ci sets this by default, set it manually for test-torture fi [ -f ~/venv/bin/activate ] && source ~/venv/bin/activate - if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test'* ]]; then + if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test1'* ]]; then locale || true export LC_ALL=C export LC_CTYPE=C diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 4ce54f39d2c5..685a533c507f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -36,7 +36,7 @@ permissions: {} # or runtime: # # - 10.7 Lion (2011) - GSS (build-time, deprecated MIT Kerberos shim) -# - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), OCSP (runtime) +# - 10.9 Mavericks (2013) - LDAP (build-time, deprecated), memset_s(), OCSP (runtime) # - 10.11 El Capitan (2015) - connectx() (runtime) # - 10.12 Sierra (2016) - clock_gettime() (build-time, runtime) # - 10.14 Mojave (2018) - SecTrustEvaluateWithError() (runtime) @@ -91,7 +91,7 @@ jobs: # shellcheck disable=SC2181 while [[ $? == 0 ]]; do for i in 1 2 3; do - if brew update && brew install automake libtool; then + if brew install automake libtool; then break 2 else echo "Error: wait to try again: $i" @@ -114,7 +114,7 @@ jobs: - name: 'cache libressl' if: ${{ contains(matrix.build.install_steps, 'libressl') }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-libressl env: cache-name: cache-libressl @@ -123,10 +123,10 @@ jobs: key: iOS-${{ env.cache-name }}-${{ env.LIBRESSL_VERSION }} - name: 'build libressl' - if: ${{ contains(matrix.build.install_steps, 'libressl') && steps.cache-libressl.outputs.cache-hit != 'true' }} + if: ${{ contains(matrix.build.install_steps, 'libressl') && !steps.cache-libressl.outputs.cache-hit }} 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" --output pkg.bin + --location --proto-redir =https "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 \ @@ -244,7 +244,7 @@ jobs: - name: 'OpenSSL libssh' compiler: llvm@18 - install: libssh libnghttp3 + install: libssh 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' @@ -275,13 +275,13 @@ jobs: install_steps: pytest 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 + -DCMAKE_C_STANDARD=90 -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - - name: 'OpenSSL SecTrust' + - name: 'OpenSSL SecTrust krb5' compiler: clang install: libnghttp3 libngtcp2 install_steps: pytest - configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm + configure: --enable-debug --with-openssl=/opt/homebrew/opt/openssl --with-ngtcp2 --with-apple-sectrust --enable-ntlm --enable-proxy-http3 --with-gssapi - name: 'OpenSSL event-based' compiler: clang @@ -293,13 +293,14 @@ jobs: install: openssl@4 libnghttp3 libngtcp2 gsasl generate: >- -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_USE_GSASL=ON -DUSE_APPLE_IDN=ON -DUSE_NGTCP2=ON -DCURL_DISABLE_VERBOSE_STRINGS=ON - -DUSE_APPLE_SECTRUST=ON -DCURL_ENABLE_NTLM=ON + -DUSE_APPLE_SECTRUST=ON -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - name: 'MultiSSL AppleIDN clang-tidy +examples' image: macos-26 compiler: clang install: llvm gnutls nettle libressl krb5 mbedtls gsasl rustls-ffi libssh fish install_steps: skiprun + CFLAGS: -Wunused-macros chkprefill: _chkprefill generate: >- -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/libressl -DCURL_DEFAULT_SSL_BACKEND=openssl @@ -316,6 +317,7 @@ jobs: compiler: clang install: llvm libnghttp3 libngtcp2 openldap krb5 install_steps: skipall + CFLAGS: -Wunused-macros generate: >- -DCURL_USE_OPENSSL=ON -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl -DUSE_NGTCP2=ON -DLDAP_INCLUDE_DIR=/opt/homebrew/opt/openldap/include @@ -324,7 +326,7 @@ jobs: -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 + -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - name: 'LibreSSL openldap krb5 c-ares +examples' compiler: clang @@ -345,16 +347,17 @@ jobs: - name: 'mbedTLS !ldap brotli zstd MultiSSL AppleIDN' compiler: llvm@18 install: brotli mbedtls zstd - install_steps: codeset-test + install_steps: codeset-test1 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 + install_steps: codeset-test2 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 + -DCURL_DISABLE_LDAP=ON -DUSE_SSLS_EXPORT=ON - name: 'aws-lc +analyzer' compiler: gcc-15 @@ -371,21 +374,21 @@ jobs: - name: 'OpenSSL torture 1' compiler: clang - install: openssl@4 libnghttp3 + install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON tflags: '-t --shallow=25 --min=480 1 to 500' - name: 'OpenSSL torture 2' compiler: clang - install: openssl@4 libnghttp3 + install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON tflags: '-t --shallow=25 --min=730 501 to 1250' - name: 'OpenSSL torture 3' compiler: clang - install: openssl@4 libnghttp3 + install: openssl@4 install_steps: torture generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@4 -DUSE_ECH=ON -DCURL_ENABLE_NTLM=ON tflags: '-t --shallow=25 --min=628 1251 to 9999' @@ -413,7 +416,7 @@ jobs: # 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 + if brew install pkgconf libpsl libssh2 ${INSTALL_PACKAGES} ${MATRIX_INSTALL}; then break 2 else echo "Error: wait to try again: $i" @@ -445,6 +448,7 @@ jobs: - name: 'configure' env: + CFLAGS: '${{ matrix.build.CFLAGS }}' MATRIX_CHKPREFILL: '${{ matrix.build.chkprefill }}' MATRIX_CONFIGURE: '${{ matrix.build.configure }}' MATRIX_GENERATE: '${{ matrix.build.generate }}' @@ -479,7 +483,6 @@ jobs: false fi else - export CFLAGS if [[ "${MATRIX_COMPILER}" = 'llvm'* ]]; then options+=" --target=$(uname -m)-apple-darwin" fi @@ -558,11 +561,20 @@ jobs: 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 + if [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test1'* ]]; then locale || true - export LC_ALL=C + unset LANG + unset LC_ALL + unset LC_COLLATE + unset LC_MESSAGES + unset LC_MONETARY + unset LC_TIME export LC_CTYPE=C export LC_NUMERIC=fr_FR.UTF-8 + elif [[ "${MATRIX_INSTALL_STEPS}" = *'codeset-test2'* ]]; then + locale || true + unset LC_ALL + export LC_TIME=fr_FR fi rm -f ~/.curlrc if [ "${MATRIX_BUILD}" = 'cmake' ]; then @@ -646,7 +658,7 @@ jobs: # Reduce build combinations, by dropping less interesting ones - { image: macos-26, compiler: 'gcc-13' } - { compiler: 'gcc-14' , build: cmake } - # Reduce autotools to just one job that is also build with cmake + # Reduce autotools to only one job that is also build with cmake - { compiler: 'gcc-13' , build: autotools } - { compiler: 'gcc-14' , build: autotools } - { compiler: 'gcc-15' , build: autotools } @@ -662,7 +674,7 @@ jobs: # shellcheck disable=SC2181 while [[ $? == 0 ]]; do for i in 1 2 3; do - if brew update && brew install automake libtool; then + if brew install automake libtool; then break 2 else echo "Error: wait to try again: $i" diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index c8a0f2156bea..621605a45ebd 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -37,204 +37,314 @@ env: DO_NOT_TRACK: '1' jobs: - netbsd: - name: 'NetBSD, CM clang openssl ${{ matrix.arch }}' + cross: + name: "${{ matrix.os }} ${{ matrix.version }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.cc }} ${{ matrix.desc }} ${{ matrix.arch }}" runs-on: ubuntu-latest - timeout-minutes: 10 + timeout-minutes: 15 + defaults: + run: + shell: cpa.sh {0} # zizmor: ignore[misfeature] + env: + CC: '${{ matrix.cc }}' + MAKEFLAGS: -j 3 + MATRIX_ARCH: '${{ matrix.arch }}' + MATRIX_BUILD: '${{ matrix.build }}' + MATRIX_INSTALL: '${{ matrix.install }}' + MATRIX_OPTIONS: '${{ matrix.options }}' + MATRIX_OS: '${{ matrix.os }}' + MATRIX_VERSION: '${{ matrix.version }}' strategy: matrix: - arch: ['x86_64'] + include: + # https://github.com/DragonFlyBSD/DPorts + # { os: 'dragonflybsd', version: '6.4.2', build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl skiprun', + # install: 'autoconf automake libtool openldap26-client libidn2', + # options: '--with-openssl --enable-ldap --enable-ldaps --with-libidn2' } + + # { os: 'dragonflybsd', version: '6.4.2', build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl skipall', + # install: 'cmake ninja' } + + # https://ports.freebsd.org/ + - { os: 'freebsd' , version: '15.1', build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'openssl', + install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', + options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } + + - { os: 'freebsd' , version: '15.1', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'openssl !unity skiprun !examples', + install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2', + options: '-DCURL_USE_GSSAPI=ON -DCMAKE_UNITY_BUILD=OFF' } + + - { os: 'freebsd' , version: '14.3', build: 'autotools', arch: 'arm64' , cc: 'clang', desc: 'openssl !examples', + install: 'autoconf automake libtool krb5-devel openldap26-client libidn2 stunnel', + options: '--with-openssl --with-gssapi --enable-ldap --enable-ldaps --with-libidn2' } + + - { os: 'freebsd' , version: '14.3', build: 'cmake' , arch: 'arm64' , cc: 'clang', desc: 'openssl', + install: 'cmake-core ninja perl5 krb5-devel openldap26-client libidn2 stunnel', + options: '-DCURL_USE_GSSAPI=ON' } + + # https://app.midnightbsd.org/ + # https://man.midnightbsd.org/cgi-bin/man.cgi/mport + # { os: 'midnightbsd' , version: '4.0.4', build: 'autotools' , arch: 'x86_64', cc: 'clang', desc: 'gnutls skipall !examples', + # install: 'autoconf autoconf-archive automake libtool gnutls', + # options: '--with-gnutls' } + + - { os: 'midnightbsd' , version: '4.0.4', build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'gnutls skiprun', + install: 'cmake-core ninja perl5 gnutls openldap26-client libidn2', + options: '-DCURL_USE_GNUTLS=ON' } + + # https://pkgsrc.se/ + - { os: 'netbsd' , version: '10.1' , build: 'autotools', arch: 'x86_64', cc: 'gcc' , desc: 'openssl skipall !examples', + install: 'autoconf automake libtool mit-krb5', + options: '--with-openssl --with-gssapi' } + + - { os: 'netbsd' , version: '10.1' , build: 'cmake' , arch: 'x86_64', cc: 'gcc' , desc: 'openssl', + install: 'cmake ninja-build mit-krb5 openldap-client libidn2', + options: '-DCURL_USE_GSSAPI=ON' } + + # https://openbsd.app/ + # https://www.openbsd.org/faq/faq15.html + # https://github.com/OpenMPT/openmpt/blob/master/.github/workflows/OpenBSD-Autotools.yml + - { os: 'openbsd' , version: '7.9' , build: 'autotools', arch: 'x86_64', cc: 'clang', desc: 'libressl skipall !examples', + install: 'autoconf-2.72p0 automake-1.18.1 libtool', # NOTE: also sync these versions with the autoreconf step! + options: '--with-openssl' } + + - { os: 'openbsd' , version: '7.9' , build: 'cmake' , arch: 'x86_64', cc: 'clang', desc: 'libressl', + install: 'cmake ninja openldap-client-- libidn2' } + + fail-fast: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: 'cmake' - uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 - env: - MATRIX_ARCH: '${{ matrix.arch }}' + + - name: 'setup VM' + uses: cross-platform-actions/action@5ea7e8e4677bd726033a10b094ba1c5762b15dee # v1.3.0 with: - environment_variables: CURL_CI CURL_TEST_MIN DO_NOT_TRACK 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 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 \ - -DCURL_WERROR=ON \ - -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::' - time cmake --build bld - time cmake --install bld - bld/src/curl --disable --version - if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU - time cmake --build bld --target testdeps - export TFLAGS='-j8' - time cmake --build bld --target test-ci + environment_variables: 'CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MAKEFLAGS MATRIX_ARCH MATRIX_BUILD MATRIX_INSTALL MATRIX_OPTIONS MATRIX_OS MATRIX_VERSION' + operating_system: '${{ matrix.os }}' + version: '${{ matrix.version }}' + architecture: '${{ matrix.arch }}' + + - name: 'install prereqs' + run: | + if [ "${MATRIX_OS}" = 'dragonflybsd' ]; then + sudo pkg install -y pkgconf brotli libnghttp2 ${MATRIX_INSTALL} + elif [ "${MATRIX_OS}" = 'freebsd' ]; then + sudo pkg install -y pkgconf brotli libnghttp2 ${MATRIX_INSTALL} + elif [ "${MATRIX_OS}" = 'midnightbsd' ]; then + if [ "${MATRIX_BUILD}" = 'autotools' ]; then + sudo mport index | grep -v -E 'Downloading.+%' + sudo mport upgrade | grep -v -E '(Downloading.+%|^/usr/local)' + fi + sudo mport install pkgconf brotli libnghttp2 ${MATRIX_INSTALL} | grep -v -E '(Downloading.+%|^/usr/local)' || true + elif [ "${MATRIX_OS}" = 'netbsd' ]; then + sudo pkgin -y install pkg-config perl brotli libssh2 libpsl nghttp2 ${MATRIX_INSTALL} + elif [ "${MATRIX_OS}" = 'openbsd' ]; then + sudo pkg_add -I brotli libssh2 libpsl nghttp2 ${MATRIX_INSTALL} + if [ "${MATRIX_BUILD}" = 'autotools' ]; then + sudo pkg_delete -I curl # to avoid autotools build linking against system libcurl fi - echo '::group::build examples' - time cmake --build bld --target curl-examples-build - echo '::endgroup::' + fi - openbsd: - name: 'OpenBSD, CM clang libressl ${{ matrix.arch }}' - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - matrix: - arch: ['x86_64'] - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - name: 'cmake' - uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 - env: - MATRIX_ARCH: '${{ matrix.arch }}' - with: - environment_variables: CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH - operating_system: 'openbsd' - version: '7.7' - architecture: ${{ matrix.arch }} - run: | - # https://openbsd.app/ - # https://www.openbsd.org/faq/faq15.html - time sudo pkg_add cmake ninja brotli openldap-client-- libssh2 libidn2 libpsl nghttp2 py3-six py3-impacket - time cmake -B bld -G Ninja \ - -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_UNITY_BUILD=ON \ - -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::' - time cmake --build bld - time cmake --install bld - bld/src/curl --disable --version - if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU - time cmake --build bld --target testdeps - export TFLAGS='-j8 !2707' # Skip 2707 'ws: Peculiar frame sizes' on suspicion of hangs - time cmake --build bld --target test-ci + - name: 'autoreconf' + if: ${{ matrix.build == 'autotools' }} + run: | + if [ "${MATRIX_OS}" = 'openbsd' ]; then + if [ "${MATRIX_VERSION}" = '7.9' ]; then + export AUTOCONF_VERSION=2.72 + export AUTOMAKE_VERSION=1.18 fi - echo '::group::build examples' - time cmake --build bld --target curl-examples-build - echo '::endgroup::' + fi + autoreconf -fi + + - name: 'configure' + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake -B bld -G Ninja -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ + -DCMAKE_C_COMPILER="${CC}" \ + -DCMAKE_UNITY_BUILD=ON -DCURL_WERROR=ON -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ + -DCURL_ENABLE_NTLM=ON ${MATRIX_OPTIONS} + else + if [ "${MATRIX_ARCH}" != 'x86_64' ]; then + options='--disable-manual --disable-docs' # Slow with autotools, skip on emulated CPU + fi + mkdir bld && cd bld + ../configure --prefix="$HOME"/curl-install --enable-unity --enable-debug --enable-warnings --enable-werror --disable-static \ + --disable-dependency-tracking --enable-option-checking=fatal \ + --with-brotli --with-libssh2 --with-nghttp2 \ + ${options} ${MATRIX_OPTIONS} + fi + + - name: 'configure log' + if: ${{ !cancelled() }} + run: cat bld/config.log bld/CMakeFiles/CMakeConfigureLog.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 V=1 + fi + + - name: 'curl -V' + run: bld/src/curl --disable --version + + - name: 'curl install' + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --install bld + else + make -C bld install + fi + + - name: 'build tests' + if: ${{ matrix.arch == 'x86_64' && !contains(matrix.desc, 'skipall') }} # Slow on emulated CPU + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --target testdeps + else + make -C bld -C tests + fi + + - name: 'run tests' + if: ${{ matrix.arch == 'x86_64' && !contains(matrix.desc, 'skipall') && !contains(matrix.desc, 'skiprun') }} # Slow on emulated CPU + run: | + export TFLAGS='-j8' + if [ "${MATRIX_OS}" = 'openbsd' ]; then + TFLAGS="$TFLAGS !2707" # Skip 2707 'ws: Peculiar frame sizes' on suspicion of hangs + fi + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --verbose --target test-ci + else + make -C bld V=1 test-ci + fi + + - name: 'build examples' + if: ${{ !contains(matrix.desc, '!examples') }} + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld --target curl-examples-build + else + make -C bld examples + fi - freebsd: - name: "FreeBSD, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }} openssl${{ matrix.desc }} ${{ matrix.arch }}" + amiga: + name: "AmigaOS, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} gcc AmiSSL m68k" runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 5 + env: + MAKEFLAGS: -j 5 + MATRIX_BUILD: '${{ matrix.build }}' + AMISSL_VERSION: '5.27' + AMISSL_SHA256: 5003bef8c5930354d16b0ce7196d71b2811891c42fad38a9238c5ce4098ad42a + TOOLCHAIN_VERSION: 6.5.0 + TOOLCHAIN_SHA256: 381e227c9ef552f073771d6f851cfdf873b574f3cf5db7c1c0107ea5d7146edc 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', desc: ' !examples' } - - { build: 'cmake' , arch: 'arm64' , compiler: 'clang' } + build: [autotools, cmake] fail-fast: false steps: + - name: 'cache compiler' + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + id: cache-compiler + with: + path: ~/opt/amiga + key: ${{ runner.os }}-amigaos-${{ env.TOOLCHAIN_VERSION }}-${{ env.AMISSL_VERSION }}-amd64 + + - name: 'install compiler' + if: ${{ !steps.cache-compiler.outputs.cache-hit }} + run: | + cd ~ + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 120 --retry 3 --retry-connrefused \ + https://franke.ms/download/amiga-gcc.tgz --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${TOOLCHAIN_SHA256}" && tar -xf pkg.bin && rm -f pkg.bin + cd opt/amiga + curl --disable --fail --silent --show-error --connect-timeout 15 --max-time 60 --retry 3 --retry-connrefused \ + --location --proto-redir =https "https://github.com/jens-maus/amissl/releases/download/${AMISSL_VERSION}/AmiSSL-${AMISSL_VERSION}-SDK.lha" --output pkg.bin + sha256sum pkg.bin | tee /dev/stderr | grep -qwF -- "${AMISSL_SHA256}" && 7z x -bd -y pkg.bin && rm -f pkg.bin + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: '${{ matrix.build }}' - uses: cross-platform-actions/action@233156312992f3f169d8d0c633c21d12a5d30455 # v1.0.0 - env: - CC: '${{ matrix.compiler }}' - MATRIX_ARCH: '${{ matrix.arch }}' - MATRIX_BUILD: '${{ matrix.build }}' - MATRIX_DESC: '${{ matrix.desc }}' - MATRIX_OPTIONS: '${{ matrix.options }}' - with: - environment_variables: CC CURL_CI CURL_TEST_MIN DO_NOT_TRACK MATRIX_ARCH MATRIX_BUILD MATRIX_DESC MATRIX_OPTIONS - operating_system: 'freebsd' - version: '14.3' - architecture: ${{ matrix.arch }} - run: | - export CURL_CI=github - - # https://ports.freebsd.org/ - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time sudo pkg install -y cmake-core ninja perl5 \ - pkgconf brotli krb5-devel openldap26-client libidn2 libnghttp2 stunnel py311-impacket - else - time sudo pkg install -y autoconf automake libtool \ - pkgconf brotli krb5-devel openldap26-client libidn2 libnghttp2 stunnel py311-impacket - export MAKEFLAGS=-j3 - fi - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake -B bld -G Ninja \ - -DCMAKE_INSTALL_PREFIX="$HOME"/curl-install \ - -DCMAKE_C_COMPILER="${CC}" \ - -DCMAKE_UNITY_BUILD=ON \ - -DCURL_WERROR=ON \ - -DENABLE_DEBUG=ON -DCMAKE_BUILD_TYPE=Debug \ - -DCURL_USE_OPENSSL=ON \ - -DCURL_USE_GSSAPI=ON \ - ${MATRIX_OPTIONS} \ - || { cat bld/CMakeFiles/CMake*.yaml; false; } - else - time autoreconf -fi - if [ "${MATRIX_ARCH}" != 'x86_64' ]; then - options='--disable-manual --disable-docs' # Slow with autotools, skip on emulated CPU - fi - mkdir bld && cd bld - 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 \ - ${options} \ - ${MATRIX_OPTIONS} \ - || { tail -n 1000 config.log; false; } - cd .. - fi + - name: 'configure' + run: | + ln -s ~/opt/amiga /opt + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake -B bld -G Ninja \ + -DAMIGA=1 \ + -DCMAKE_SYSTEM_NAME=Generic \ + -DCMAKE_SYSTEM_PROCESSOR=m68k \ + -DCMAKE_C_COMPILER_TARGET=m68k-unknown-amigaos \ + -DCMAKE_C_COMPILER=/opt/amiga/bin/m68k-amigaos-gcc \ + -DCMAKE_C_FLAGS='-O0 -msoft-float -mcrt=clib2' \ + -DCMAKE_UNITY_BUILD=ON \ + -DCURL_WERROR=ON \ + -DCURL_USE_LIBPSL=OFF \ + -DAMISSL_INCLUDE_DIR=/opt/amiga/AmiSSL/Developer/include \ + -DAMISSL_STUBS_LIBRARY=/opt/amiga/AmiSSL/Developer/lib/AmigaOS3/libamisslstubs.a \ + -DAMISSL_AUTO_LIBRARY=/opt/amiga/AmiSSL/Developer/lib/AmigaOS3/libamisslauto.a + else + autoreconf -fi + mkdir bld && cd bld && ../configure --enable-unity --enable-warnings --enable-werror \ + --disable-dependency-tracking --enable-option-checking=fatal \ + CC=/opt/amiga/bin/m68k-amigaos-gcc \ + AR=/opt/amiga/bin/m68k-amigaos-ar \ + RANLIB=/opt/amiga/bin/m68k-amigaos-ranlib \ + --host=m68k-amigaos \ + --disable-shared \ + --without-libpsl \ + --with-amissl \ + LDFLAGS=-L/opt/amiga/AmiSSL/Developer/lib/AmigaOS3 \ + CPPFLAGS=-I/opt/amiga/AmiSSL/Developer/include \ + CFLAGS='-O0 -msoft-float -mcrt=clib2' \ + LIBS='-lnet -lm -latomic' + fi - 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::' + - name: 'configure log' + if: ${{ !cancelled() }} + run: cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake --build bld - time cmake --install bld - else - time make -C bld install - fi + - 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 - bld/src/curl --disable --version - - if [ "${MATRIX_ARCH}" = 'x86_64' ]; then # Slow on emulated CPU - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake --build bld --target testdeps - else - time make -C bld -C tests - fi - if [ "${MATRIX_DESC#*!runtests*}" = "${MATRIX_DESC}" ]; then - export TFLAGS='-j8' - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake --build bld --verbose --target test-ci - else - time make -C bld V=1 test-ci - fi - fi - fi + - name: 'build' + run: | + if [ "${MATRIX_BUILD}" = 'cmake' ]; then + cmake --build bld + else + make -C bld + fi - if [ "${MATRIX_DESC#*!examples*}" = "${MATRIX_DESC}" ]; then - echo '::group::build examples' - if [ "${MATRIX_BUILD}" = 'cmake' ]; then - time cmake --build bld --target curl-examples-build - else - time make -C bld examples - fi - echo '::endgroup::' - fi + - name: 'curl info' + run: | + find . -type f \( -name curl -o -name '*.a' \) -print0 | xargs -0 file -- + find . -type f \( -name curl -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 android: name: "Android ${{ matrix.platform }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.name }} arm64" @@ -297,6 +407,12 @@ jobs: if: ${{ !cancelled() }} run: cat bld/config.log bld/CMakeFiles/CMake*.yaml 2>/dev/null || true + - name: 'dump config files' + run: | + for f in libcurl.pc curl-config; do + echo "::group::${f}"; grep -v '^#' bld/"${f}" || true; echo '::endgroup::' + done + - name: 'curl_config.h' run: | echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' @@ -340,7 +456,7 @@ jobs: 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_VERSION: '3.4' TOOLCHAIN_SHA256: 8464f17017d6ab1b2bb2df4ed82357b5bf692e6e2b7fee37e315638f3d505f00 strategy: matrix: @@ -350,22 +466,24 @@ jobs: - name: 'install packages' timeout-minutes: 2 run: | + sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt + sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install libfl2 - name: 'cache compiler (djgpp)' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 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' }} + if: ${{ !steps.cache-compiler.outputs.cache-hit }} 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 + --location --proto-redir =https "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 \ diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 6b4366a7f916..53429d1f2bcf 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -39,7 +39,7 @@ env: OPENSSH_WINDOWS_VERSION: 10.0.0.0p2-Preview OPENSSH_WINDOWS_SHA256_ARM64: 698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42 OPENSSH_WINDOWS_SHA256_WIN64: 23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5 - STUNNEL_VERSION: 5.78 + STUNNEL_VERSION: '5.78' STUNNEL_SHA256: 32a88dcc5654f955266109be8bf10fd7d56fa4e125cab821ee508230570e46c5 jobs: @@ -51,7 +51,7 @@ jobs: image: [windows-11-arm, windows-2022] # Cannot share cache between arm and intel: https://github.com/actions/cache/issues/1622 steps: - name: 'cache test prereqs (stunnel)' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -59,7 +59,7 @@ jobs: lookup-only: true - name: 'install test prereqs (stunnel)' - if: ${{ steps.cache-stunnel.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-stunnel.outputs.cache-hit }} timeout-minutes: 2 shell: bash run: | @@ -93,10 +93,9 @@ jobs: 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: - - uses: cygwin/cygwin-install-action@711d29f3da23c9f4a1798e369a6f01198c13b11a # v6.1 + - uses: cygwin/cygwin-install-action@3f0a3f9f988f7e96b8c18098ae05eaec175f5b52 # v6.0.2 with: platform: ${{ matrix.platform }} work-vol: 'D:' @@ -192,7 +191,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -302,7 +301,7 @@ jobs: 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', + config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DCURL_USE_OPENSSL=ON -DENABLE_UNICODE=OFF -DUSE_NGTCP2=ON -DUSE_PROXY_HTTP3=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' , @@ -322,7 +321,7 @@ jobs: install: 'mingw-w64-i686-c-ares mingw-w64-i686-gnutls mingw-w64-i686-libssh2 mingw-w64-i686-openssl' } fail-fast: false steps: - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 if: ${{ matrix.sys == 'msys' }} with: msystem: ${{ matrix.sys }} @@ -338,7 +337,7 @@ jobs: libpsl-devel ${{ matrix.install }} - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 if: ${{ matrix.sys != 'msys' }} with: msystem: ${{ matrix.sys }} @@ -463,7 +462,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -486,11 +485,11 @@ jobs: 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 + --location --proto-redir =https "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 + --location --proto-redir =https "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 @@ -522,11 +521,9 @@ jobs: fi fi if [ -n "${MATRIX_OPENSSH}" ]; then # OpenSSH-Windows - TFLAGS+=' ~601 ~603 ~617 ~619 ~621 ~641 ~665 ~2004' # SCP + TFLAGS+=' ~601 ~603 ~617 ~619 ~621 ~641 ~665 ~2004 ~3022' # 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 @@ -577,13 +574,13 @@ jobs: strategy: matrix: include: - - name: 'schannel +analyzer' # mingw-w64 12.0 + - name: 'schannel +analyzer' # mingw-w64 14.0 sys: 'mingw64' dir: 'w64devkit' env: 'x86_64' - ver: '15.1.0' - url: 'https://github.com/skeeto/w64devkit/releases/download/v2.2.0/w64devkit-x64-2.2.0.7z.exe' - SHA256: e02de30b97196329662007d64bc4509fbd7f5e14339d344075c7f1223dead4a2 + ver: '16.1.0' + url: 'https://github.com/skeeto/w64devkit/releases/download/v2.8.0/w64devkit-x64-2.8.0.7z.exe' + SHA256: 6252bf34fe2231a55ac7f03d482b36d2c7c58697990551bba508102cfb3f342e 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' - name: 'schannel' # mingw-w64 10.0 @@ -631,7 +628,7 @@ jobs: chkprefill: '' # Set it once to silence actionlint fail-fast: false steps: - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 with: msystem: ${{ matrix.sys }} release: false @@ -643,14 +640,14 @@ jobs: ${{ matrix.install }} - name: 'cache compiler (gcc ${{ matrix.ver }}-${{ matrix.env }})' - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-compiler with: path: D:\my-cache key: ${{ runner.os }}-mingw-w64-${{ matrix.ver }}-${{ matrix.env }} - name: 'install compiler (gcc ${{ matrix.ver }}-${{ matrix.env }})' - if: ${{ steps.cache-compiler.outputs.cache-hit != 'true' }} + if: ${{ !steps.cache-compiler.outputs.cache-hit }} timeout-minutes: 5 env: MATRIX_URL: '${{ matrix.url }}' @@ -727,7 +724,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -738,7 +735,7 @@ jobs: if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} timeout-minutes: 2 run: | - if "bld/src/curl.exe" --disable -V 2>/dev/null | grep smb; then + 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 @@ -770,7 +767,7 @@ jobs: linux-cross-mingw-w64: name: "linux-mingw, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }}" - runs-on: ubuntu-latest + runs-on: ubuntu-26.04 timeout-minutes: 10 env: LDFLAGS: -s @@ -784,14 +781,17 @@ jobs: include: - { build: 'autotools', compiler: 'gcc' } - { build: 'cmake' , compiler: 'gcc' } - - { build: 'cmake' , compiler: 'clang-tidy', install_packages: 'clang-20 clang-tidy-20' } + - { build: 'cmake' , compiler: 'clang-tidy', install_packages: 'clang-22 clang-tidy-22', CFLAGS: '-Wunused-macros' } steps: - name: 'install packages' timeout-minutes: 2 env: MATRIX_INSTALL_PACKAGES: '${{ matrix.install_packages }}' run: | + ls -l /etc/apt/sources.list.d + sudo find /etc/apt/sources.list.d -type f -not -name 'ubuntu.sources' -delete -print sudo sed -i 's/priority:1/priority:9/' /etc/apt/apt-mirrors.txt; cat /etc/apt/apt-mirrors.txt + sudo apt-get -o Dpkg::Use-Pty=0 update sudo apt-get -o Dpkg::Use-Pty=0 install gcc-mingw-w64-x86-64-win32 ${MATRIX_INSTALL_PACKAGES} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -803,13 +803,15 @@ jobs: run: autoreconf -fi - name: 'configure' + env: + CFLAGS: '${{ matrix.CFLAGS }}' run: | if [ "${MATRIX_BUILD}" = 'cmake' ]; then if [ "${MATRIX_COMPILER}" = 'clang-tidy' ]; then - options+=' -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-20' + options+=' -DCURL_CLANG_TIDY=ON -DCLANG_TIDY=/usr/bin/clang-tidy-22' options+=' -DENABLE_UNICODE=ON -DUSE_SSLS_EXPORT=ON' - options+=' -DCMAKE_C_COMPILER=clang-20' - options+=" -DCMAKE_RC_COMPILER=llvm-windres-$(clang-20 -dumpversion | cut -d '.' -f 1)" + options+=' -DCMAKE_C_COMPILER=clang-22' + options+=" -DCMAKE_RC_COMPILER=llvm-windres-$(clang-22 -dumpversion | cut -d '.' -f 1)" else options+=" -DCMAKE_C_COMPILER=${TRIPLET}-gcc" fi @@ -922,7 +924,7 @@ jobs: env: 'ucrt-x86_64' plat: 'windows' type: 'Debug' - image: 'windows-2025-vs2026' + image: 'windows-2025' chkprefill: '_chkprefill' tflags: '--min=1850' config: >- @@ -951,7 +953,7 @@ jobs: -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 + -DCURL_ENABLE_NTLM=ON -DUSE_PROXY_HTTP3=ON - name: 'schannel U' install-vcpkg: 'zlib libssh2[core,zlib]' @@ -971,7 +973,7 @@ jobs: fail-fast: false steps: - - uses: msys2/setup-msys2@cafece8e6baf9247cf9b1bf95097b0b983cc558d # v2.31.0 + - uses: msys2/setup-msys2@e9898307ac31d1a803454791be09ab9973336e1c # v2.31.1 with: msystem: ${{ matrix.arch == 'arm64' && 'clangarm64' || 'ucrt64' }} release: ${{ contains(matrix.image, 'arm') }} @@ -1030,7 +1032,7 @@ jobs: # 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' + [[ "${MATRIX_IMAGE}" = *'windows-2025'* ]] && 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/$MSVC_EDITION")" -mindepth 1 -maxdepth 1 -type d -name '*.*' | sort | tail -n 1)" @@ -1117,7 +1119,7 @@ jobs: - name: 'cache test prereqs (stunnel)' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 id: cache-stunnel with: path: C:\my-stunnel @@ -1140,11 +1142,11 @@ jobs: 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 + --location --proto-redir =https "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 + --location --proto-redir =https "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 diff --git a/.mailmap b/.mailmap index d8558a2ec3e3..20384c010100 100644 --- a/.mailmap +++ b/.mailmap @@ -84,8 +84,8 @@ Tobias Nyholm Timur Artikov Michał Antoniak <47522782+MAntoniak@users.noreply.github.com> Gleb Ivanovsky -Max Dymond -Max Dymond +Max Dymond +Max Dymond Abhinav Singh Malik Idrees Hasan Khan <77000356+MalikIdreesHasanKhan@users.noreply.github.com> Yongkang Huang @@ -122,3 +122,4 @@ Stephen Farrell Calvin Ruocco Hamza Bensliman Kaixuan Li +Darren Banfi diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 2cf306b58853..be3b6f73eee9 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -283,7 +283,7 @@ static void check(char c) int main(void) { char buffer[1024]; - /* This will not compile if strerror_r does not return a char* */ + /* This does not compile if strerror_r does not return a char* */ /* !checksrc! disable ERRNOVAR 1 */ check(strerror_r(EACCES, buffer, sizeof(buffer))[0]); return 0; @@ -303,7 +303,7 @@ static void check(float f) int main(void) { char buffer[1024]; - /* This will not compile if strerror_r does not return an int */ + /* This does not compile if strerror_r does not return an int */ /* !checksrc! disable ERRNOVAR 1 */ check(strerror_r(EACCES, buffer, sizeof(buffer))); return 0; diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index 9237fb30b192..6f352b2e4f67 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -25,15 +25,14 @@ # # Input variables: # -# - `GSS_ROOT_DIR`: Absolute path 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) # # Defines: # -# - `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. +# - `GSS_FOUND`: System has GSS. +# - `GSS_VERSION`: Version of GSS. +# - `CURL::gss`: GSS library target. +# - `INTERFACE_CURL_GSS_FLAVOR`: Custom property. "GNU" or "MIT" if detected. set(_gnu_modname "gss") set(_mit_modname "mit-krb5-gssapi") @@ -51,7 +50,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}) + pkg_search_module(_gss ${_mit_modname} ${_gnu_modname}) list(APPEND _gss_root_hints "${_gss_PREFIX}") set(_gss_version "${_gss_VERSION}") endif() @@ -121,7 +120,7 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr RESULT_VARIABLE _gss_configure_failed OUTPUT_STRIP_TRAILING_WHITESPACE) - # Older versions may not have the "--version" parameter. In this case we just do not care. + # Older versions may not have the "--version" parameter. In this case we do not care. if(_gss_configure_failed) set(_gss_version 0) else() @@ -134,9 +133,9 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr RESULT_VARIABLE _gss_configure_failed OUTPUT_STRIP_TRAILING_WHITESPACE) - # Older versions may not have the "--vendor" parameter. In this case we just do not care. + # Older versions may not have the "--vendor" parameter. In this case we do not care. if(NOT _gss_configure_failed AND NOT _gss_vendor MATCHES "Heimdal|heimdal") - set(_gss_flavour "MIT") # assume a default, should not really matter + set(_gss_flavor "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?) @@ -152,7 +151,7 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr cmake_pop_check_state() if(_gss_have_mit_headers) - set(_gss_flavour "MIT") + set(_gss_flavor "MIT") if(WIN32) if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND _gss_libdir_suffixes "lib/AMD64") @@ -170,14 +169,14 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr 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_flavor "GNU") set(_gss_pc_requires ${_gnu_modname}) set(_gss_libname "gss") endif() endif() # If we have headers, look up libraries - if(_gss_flavour) + if(_gss_flavor) 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) @@ -189,28 +188,26 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr find_library(_gss_LIBRARIES NAMES ${_gss_libname} HINTS ${_gss_libdir_hints} PATH_SUFFIXES ${_gss_libdir_suffixes}) endif() endif() - if(NOT _gss_flavour) + if(NOT _gss_flavor) message(FATAL_ERROR "GNU or MIT GSS is required") endif() else() - # _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") + if(_gss_MODULE_NAME STREQUAL _gnu_modname) + set(_gss_flavor "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") + elseif(_gss_MODULE_NAME STREQUAL _mit_modname) + set(_gss_flavor "MIT") set(_gss_pc_requires ${_mit_modname}) else() 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_flavor} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")") endif() set(GSS_VERSION ${_gss_version}) if(NOT GSS_VERSION) - if(_gss_flavour STREQUAL "MIT") + if(_gss_flavor STREQUAL "MIT" AND WIN32) 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") @@ -218,12 +215,8 @@ if(NOT GSS_VERSION) get_filename_component(_mit_version "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME CACHE) endif() - if(WIN32 AND _mit_version) - set(GSS_VERSION "${_mit_version}") - else() - set(GSS_VERSION "MIT Unknown") - endif() - else() # GNU + set(GSS_VERSION "${_mit_version}") + elseif(_gss_flavor STREQUAL "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}") @@ -238,7 +231,7 @@ endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GSS REQUIRED_VARS - _gss_flavour + _gss_flavor _gss_LIBRARIES VERSION_VAR GSS_VERSION @@ -261,7 +254,7 @@ 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_CURL_GSS_FLAVOR "${_gss_flavor}" INTERFACE_LIBCURL_PC_MODULES "${_gss_pc_requires}" INTERFACE_COMPILE_OPTIONS "${_gss_CFLAGS}" INTERFACE_INCLUDE_DIRECTORIES "${_gss_INCLUDE_DIRS}" diff --git a/CMake/FindNGHTTP2.cmake b/CMake/FindNGHTTP2.cmake index f93113f404e1..bca7cf8a8181 100644 --- a/CMake/FindNGHTTP2.cmake +++ b/CMake/FindNGHTTP2.cmake @@ -61,7 +61,7 @@ if(_nghttp2_FOUND) elseif(nghttp2_CONFIG) set(NGHTTP2_FOUND TRUE) set(NGHTTP2_VERSION ${nghttp2_VERSION}) - if(NGHTTP2_USE_STATIC_LIBS) + if(NGHTTP2_USE_STATIC_LIBS OR NOT TARGET nghttp2::nghttp2) set(_nghttp2_LIBRARIES nghttp2::nghttp2_static) else() set(_nghttp2_LIBRARIES nghttp2::nghttp2) diff --git a/CMake/FindNGHTTP3.cmake b/CMake/FindNGHTTP3.cmake index 427c139f21c4..ed671b7a4929 100644 --- a/CMake/FindNGHTTP3.cmake +++ b/CMake/FindNGHTTP3.cmake @@ -61,7 +61,7 @@ if(_nghttp3_FOUND) elseif(nghttp3_CONFIG) set(NGHTTP3_FOUND TRUE) set(NGHTTP3_VERSION ${nghttp3_VERSION}) - if(NGHTTP3_USE_STATIC_LIBS) + if(NGHTTP3_USE_STATIC_LIBS OR NOT TARGET nghttp3::nghttp3) set(_nghttp3_LIBRARIES nghttp3::nghttp3_static) else() set(_nghttp3_LIBRARIES nghttp3::nghttp3) diff --git a/CMake/FindNGTCP2.cmake b/CMake/FindNGTCP2.cmake index e4929163777c..bf2f48877749 100644 --- a/CMake/FindNGTCP2.cmake +++ b/CMake/FindNGTCP2.cmake @@ -49,27 +49,28 @@ # # - `NGTCP2_FOUND`: System has ngtcp2. # - `NGTCP2_VERSION`: Version of ngtcp2. +# - `NGTCP2_CRYPTO_BACKEND`: Name of the crypto library component. (Empty if COMPONENTS was not used.) # - `CURL::ngtcp2`: ngtcp2 library target. +set(NGTCP2_CRYPTO_BACKEND "") if(NGTCP2_FIND_COMPONENTS) - set(_ngtcp2_crypto_backend "") foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS) if(_component MATCHES "^(BoringSSL|GnuTLS|LibreSSL|ossl|quictls|wolfSSL)") - if(_ngtcp2_crypto_backend) + if(NGTCP2_CRYPTO_BACKEND) message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected") endif() - set(_ngtcp2_crypto_backend ${_component}) + set(NGTCP2_CRYPTO_BACKEND ${_component}) endif() endforeach() - if(_ngtcp2_crypto_backend) - string(TOLOWER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library_lower) - string(TOUPPER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library_upper) + if(NGTCP2_CRYPTO_BACKEND) + string(TOLOWER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library_lower) + string(TOUPPER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library_upper) endif() endif() set(_ngtcp2_pc_requires "libngtcp2") -if(_ngtcp2_crypto_backend) +if(NGTCP2_CRYPTO_BACKEND) list(APPEND _ngtcp2_pc_requires "lib${_crypto_library_lower}") endif() @@ -81,7 +82,7 @@ if(NOT DEFINED NGTCP2_INCLUDE_DIR AND pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires}) set(_tried_pkgconfig TRUE) endif() - if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG) + if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG AND NGTCP2_CRYPTO_BACKEND) find_package(ngtcp2 CONFIG QUIET) # Skip using it if the crypto library target is not available if(ngtcp2_CONFIG AND @@ -105,7 +106,7 @@ if(_ngtcp2_FOUND) elseif(ngtcp2_CONFIG) set(NGTCP2_FOUND TRUE) set(NGTCP2_VERSION ${ngtcp2_VERSION}) - if(NGTCP2_USE_STATIC_LIBS) + if(NGTCP2_USE_STATIC_LIBS OR NOT TARGET ngtcp2::ngtcp2) set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2_static ngtcp2::${_crypto_library_lower}_static) else() set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2 ngtcp2::${_crypto_library_lower}) @@ -130,7 +131,7 @@ else() unset(_version_str) endif() - if(_ngtcp2_crypto_backend) + if(NGTCP2_CRYPTO_BACKEND) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20) cmake_path(GET NGTCP2_LIBRARY PARENT_PATH _ngtcp2_library_dir) else() @@ -145,7 +146,7 @@ else() endif() if(${_crypto_library_upper}_LIBRARY) - set(NGTCP2_${_ngtcp2_crypto_backend}_FOUND TRUE) + set(NGTCP2_${NGTCP2_CRYPTO_BACKEND}_FOUND TRUE) set(NGTCP2_CRYPTO_LIBRARY ${${_crypto_library_upper}_LIBRARY}) endif() endif() diff --git a/CMake/FindZstd.cmake b/CMake/FindZstd.cmake index 8dc620a1009f..176645d97bab 100644 --- a/CMake/FindZstd.cmake +++ b/CMake/FindZstd.cmake @@ -53,12 +53,12 @@ if(NOT DEFINED ZSTD_INCLUDE_DIR AND pkg_check_modules(_zstd ${_zstd_pc_requires}) endif() if(NOT _zstd_FOUND AND CURL_USE_CMAKECONFIG) - find_package(Zstd CONFIG QUIET) + find_package(zstd CONFIG QUIET) # Skip using if older than v1.4.5 - if(Zstd_CONFIG AND + if(zstd_CONFIG AND NOT TARGET zstd::libzstd_static AND NOT TARGET zstd::libzstd_shared) - unset(Zstd_CONFIG) + unset(zstd_CONFIG) endif() endif() endif() @@ -74,9 +74,10 @@ if(_zstd_FOUND) set(_zstd_LIBRARIES "${_zstd_STATIC_LIBRARIES}") endif() message(STATUS "Found Zstd (via pkg-config): ${_zstd_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")") -elseif(Zstd_CONFIG) +elseif(zstd_CONFIG) + set(Zstd_FOUND TRUE) set(ZSTD_FOUND TRUE) - set(ZSTD_VERSION ${Zstd_VERSION}) + set(ZSTD_VERSION ${zstd_VERSION}) if(ZSTD_USE_STATIC_LIBS) set(_zstd_LIBRARIES zstd::libzstd_static) elseif(TARGET zstd::libzstd) @@ -84,7 +85,7 @@ elseif(Zstd_CONFIG) else() set(_zstd_LIBRARIES zstd::libzstd_shared) endif() - message(STATUS "Found Zstd (via CMake Config): ${Zstd_CONFIG} (found version \"${ZSTD_VERSION}\")") + message(STATUS "Found Zstd (via CMake Config): ${zstd_CONFIG} (found version \"${ZSTD_VERSION}\")") else() find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h") if(ZSTD_USE_STATIC_LIBS) diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index 5e26c384696f..953c1c6ee6dd 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -267,6 +267,8 @@ macro(curl_collect_target_link_options _target) get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES) if(_val) foreach(_lib IN LISTS _val) + # Extract imported target name from e.g. "$" set by libssh2 + string(REGEX REPLACE "^\\\$\$" "\\1" _lib "${_lib}") if(TARGET "${_lib}") curl_collect_target_link_options(${_lib}) else() diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index aca79ec947b7..1ce5f7bda620 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -250,7 +250,7 @@ if(PICKY_COMPILER) 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 + -Wformat-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 @@ -300,13 +300,14 @@ if(PICKY_COMPILER) list(APPEND _picky_enable -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.1 -Wformat=2 # clang 2.7 gcc 4.8 + -Wlogical-op # gcc 4.4 -Wtrampolines # gcc 4.6 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0) list(APPEND _picky_enable -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 + -Wformat-signedness # clang 19.1 gcc 5.1 appleclang 17.0 ) endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0) @@ -391,7 +392,7 @@ if(PICKY_COMPILER) list(APPEND _picky "-Wno-conversion") # Avoid false positives endif() endif() - elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1950) # Skip for untested/unreleased newer versions + elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1951) # Enable for tested versions only 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)' @@ -407,7 +408,7 @@ if(PICKY_COMPILER) list(APPEND _picky "-wd4746") 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 + list(APPEND _picky "-wd5045") # Compiler inserts Spectre mitigation for memory load if /Qspectre switch specified endif() endif() endif() @@ -437,7 +438,7 @@ if(CMAKE_C_STANDARD STREQUAL 90) 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 + list(APPEND _picky "-Wno-comma") # Silly endif() endif() diff --git a/CMake/curl-config.in.cmake b/CMake/curl-config.in.cmake index 317477197f51..b4300ca2108c 100644 --- a/CMake/curl-config.in.cmake +++ b/CMake/curl-config.in.cmake @@ -122,7 +122,7 @@ if("@USE_NGHTTP3@") list(APPEND _curl_libs CURL::nghttp3) endif() if("@USE_NGTCP2@") - find_dependency(NGTCP2 MODULE) + find_dependency(NGTCP2 MODULE COMPONENTS "@NGTCP2_CRYPTO_BACKEND@") list(APPEND _curl_libs CURL::ngtcp2) endif() if("@USE_GNUTLS@") diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 8ecd20618645..78816499ca01 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -21,7 +21,7 @@ # SPDX-License-Identifier: curl # ########################################################################### -# Based on CI runs for Cygwin/MSYS2, Linux, macOS, FreeBSD, NetBSD, OpenBSD +# Based on CI runs for Cygwin/MSYS2, Linux, macOS/iOS, DragonFly BSD, FreeBSD, MidnightBSD, NetBSD, OpenBSD if(NOT UNIX) message(FATAL_ERROR "This file should be included on Unix platforms only") endif() @@ -30,7 +30,9 @@ if(APPLE OR CYGWIN) set(HAVE_ACCEPT4 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_ACCEPT4 1) @@ -58,13 +60,26 @@ set(HAVE_DECL_FSEEKO 1) set(HAVE_DIRENT_H 1) if(APPLE OR CYGWIN OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_EVENTFD 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(HAVE_EVENTFD 1) endif() +if(ANDROID AND ANDROID_PLATFORM_LEVEL GREATER_EQUAL 34) + set(HAVE_MEMSET_EXPLICIT 1) +endif() +if((APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.9) OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR # v6+ + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR # v11.2+ + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") # v1.3+ + set(HAVE_MEMSET_S 1) +elseif(NOT APPLE) + set(HAVE_MEMSET_S 0) +endif() set(HAVE_FCNTL 1) set(HAVE_FCNTL_H 1) set(HAVE_FCNTL_O_NONBLOCK 1) @@ -76,7 +91,9 @@ if(APPLE) set(HAVE_FSETXATTR 1) set(HAVE_FSETXATTR_5 0) set(HAVE_FSETXATTR_6 1) -elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR +elseif(CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_FSETXATTR 0) set(HAVE_FSETXATTR_5 0) @@ -93,7 +110,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETADDRINFO_THREADSAFE 0) elseif(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(HAVE_GETADDRINFO_THREADSAFE 1) endif() @@ -104,14 +123,18 @@ if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETHOSTBYNAME_R 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR - CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") set(HAVE_GETHOSTBYNAME_R 1) endif() set(HAVE_GETHOSTBYNAME_R_3 0) set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0) set(HAVE_GETHOSTBYNAME_R_5 0) set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") set(HAVE_GETHOSTBYNAME_R_6 1) set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 1) else() @@ -127,7 +150,9 @@ endif() if(APPLE OR CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETPASS_R 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") @@ -197,6 +222,7 @@ 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 "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS") @@ -217,7 +243,8 @@ set(HAVE_SCHED_YIELD 1) set(HAVE_SELECT 1) set(HAVE_SEND 1) if(APPLE OR - CYGWIN) + CYGWIN OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD") set(HAVE_SENDMMSG 0) else() set(HAVE_SENDMMSG 1) @@ -255,10 +282,12 @@ if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS") endif() if(APPLE OR CYGWIN OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_SYS_EVENTFD_H 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD") set(HAVE_SYS_EVENTFD_H 1) endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dd9c7aa7027..e459b3d0761a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -358,14 +358,14 @@ elseif(BUILD_STATIC_CURL AND NOT BUILD_STATIC_LIBS) set(BUILD_STATIC_CURL OFF) endif() -# Lib flavour selected for curl tool +# Lib flavor selected for curl tool if(BUILD_STATIC_CURL) set(LIB_SELECTED_FOR_EXE ${LIB_STATIC}) else() set(LIB_SELECTED_FOR_EXE ${LIB_SHARED}) endif() -# Lib flavour selected for example and test programs. +# Lib flavor selected for example and test programs. if(BUILD_SHARED_LIBS) set(LIB_SELECTED ${LIB_SHARED}) else() @@ -592,7 +592,7 @@ option(BUILD_MISC_DOCS "Build misc man pages (e.g. curl-config and mk-ca-bundle) 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) - message(WARNING "Perl not found. Will not build manuals.") + message(WARNING "Perl not found. Cannot build manuals.") endif() # If we are on AIX, do the _ALL_SOURCE magic @@ -681,6 +681,7 @@ elseif(AMIGA) set(OPENSSL_CRYPTO_LIBRARY "${AMISSL_AUTO_LIBRARY}") set(CURL_USE_OPENSSL ON) set(CURL_CA_FALLBACK ON CACHE BOOL "") + list(PREPEND CURL_NETWORK_AND_TIME_LIBS "${AMISSL_STUBS_LIBRARY}" "${AMISSL_AUTO_LIBRARY}") endif() elseif(NOT WIN32 AND NOT APPLE) check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) @@ -738,7 +739,7 @@ endif() cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF) cmake_dependent_option(CURL_USE_GNUTLS "Enable GnuTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) -cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENABLE_SSL OFF) +cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS (experimental)" OFF CURL_ENABLE_SSL OFF) if(WIN32 OR CURL_USE_SCHANNEL OR @@ -835,26 +836,26 @@ if(CURL_USE_OPENSSL) cmake_push_check_state() list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) - if(NOT DEFINED HAVE_BORINGSSL) - check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) - endif() if(NOT DEFINED HAVE_AWSLC) check_symbol_exists("OPENSSL_IS_AWSLC" "openssl/base.h" HAVE_AWSLC) endif() + if(NOT DEFINED HAVE_BORINGSSL) + check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) + endif() if(NOT DEFINED HAVE_LIBRESSL) check_symbol_exists("LIBRESSL_VERSION_NUMBER" "openssl/opensslv.h" HAVE_LIBRESSL) endif() cmake_pop_check_state() - if(HAVE_BORINGSSL OR HAVE_AWSLC) - if(NOT MSVC AND NOT ANDROID) # BoringSSL/AWS-LC MSVC builds use native Windows threads + if(HAVE_AWSLC OR HAVE_BORINGSSL) + if(NOT MSVC AND NOT ANDROID) # AWS-LC/BoringSSL 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.") + message(WARNING "AWS-LC/BoringSSL requires POSIX Threads.") endif() endif() if(OPENSSL_USE_STATIC_LIBS AND CMAKE_C_COMPILER_ID MATCHES "Clang") @@ -863,17 +864,17 @@ if(CURL_USE_OPENSSL) endif() endif() - if(HAVE_BORINGSSL) + if(USE_AMISSL) + set(_openssl "AmiSSL") + elseif(HAVE_AWSLC) + set(_openssl "AWS-LC") + elseif(HAVE_BORINGSSL) if(BORINGSSL_VERSION) set(CURL_BORINGSSL_VERSION "\"${BORINGSSL_VERSION}\"") endif() set(_openssl "BoringSSL") - elseif(HAVE_AWSLC) - set(_openssl "AWS-LC") elseif(HAVE_LIBRESSL) set(_openssl "LibreSSL") - elseif(USE_AMISSL) - set(_openssl "AmiSSL") else() set(_openssl "OpenSSL") endif() @@ -1080,8 +1081,8 @@ if(USE_OPENSSL) endif() endif() -option(USE_HTTPSRR "Enable HTTPS RR support" OFF) -option(USE_ECH "Enable ECH support" OFF) +option(USE_HTTPSRR "Enable HTTPS RR support (experimental)" OFF) +option(USE_ECH "Enable ECH support (experimental)" OFF) if(USE_ECH) if(USE_OPENSSL OR USE_WOLFSSL OR USE_RUSTLS) # Be sure that the TLS library actually supports ECH. @@ -1097,7 +1098,7 @@ if(USE_ECH) set(HAVE_ECH 1) endif() if(NOT HAVE_ECH) - message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL/rustls-ffi") + message(FATAL_ERROR "ECH support missing in AWS-LC/BoringSSL/OpenSSL/Rustls/wolfSSL") else() message(STATUS "ECH enabled") # ECH wants HTTPSRR @@ -1105,11 +1106,11 @@ if(USE_ECH) message(STATUS "HTTPSRR enabled") endif() else() - message(FATAL_ERROR "ECH requires ECH-enabled OpenSSL, BoringSSL, AWS-LC, wolfSSL or rustls-ffi") + message(FATAL_ERROR "ECH requires ECH-enabled AWS-LC, BoringSSL, OpenSSL, Rustls or wolfSSL") endif() endif() -option(USE_SSLS_EXPORT "Enable SSL session export support" OFF) +option(USE_SSLS_EXPORT "Enable SSL session import/export (experimental)" OFF) if(USE_SSLS_EXPORT) if(_ssl_enabled) message(STATUS "SSL export enabled.") @@ -1118,6 +1119,8 @@ if(USE_SSLS_EXPORT) endif() endif() +option(USE_PROXY_HTTP3 "Enable HTTP/3 proxy support (experimental)" OFF) + option(USE_NGHTTP2 "Use nghttp2 library" ON) if(USE_NGHTTP2) find_package(NGHTTP2 MODULE) @@ -1165,7 +1168,7 @@ if(USE_NGTCP2) list(APPEND CURL_LIBS CURL::nghttp3) endif() -option(USE_QUICHE "Use quiche library for HTTP/3 support" OFF) +option(USE_QUICHE "Use quiche library for HTTP/3 support (experimental)" OFF) if(USE_QUICHE) if(USE_NGTCP2) message(FATAL_ERROR "Only one HTTP/3 backend can be selected") @@ -1186,6 +1189,20 @@ if(USE_QUICHE) endif() endif() +if(USE_PROXY_HTTP3) + if(CURL_DISABLE_PROXY) + message(FATAL_ERROR "USE_PROXY_HTTP3 requires proxy support") + elseif(CURL_DISABLE_HTTP) + message(FATAL_ERROR "USE_PROXY_HTTP3 requires HTTP support") + elseif(NOT USE_NGTCP2 OR NOT USE_NGHTTP3) + message(FATAL_ERROR "USE_PROXY_HTTP3 requires ngtcp2 + nghttp3") + elseif(NOT USE_OPENSSL) + message(FATAL_ERROR "USE_PROXY_HTTP3 currently requires OpenSSL") + else() + message(STATUS "HTTP/3 proxy support enabled (experimental)") + endif() +endif() + if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP)) set(USE_TLS_SRP 1) endif() @@ -1333,8 +1350,8 @@ if(CURL_USE_GSSAPI) if(GSS_FOUND) list(APPEND CURL_LIBS CURL::gss) - get_target_property(_gss_flavour CURL::gss INTERFACE_CURL_GSS_FLAVOUR) - if(_gss_flavour STREQUAL "GNU") + get_target_property(_gss_flavor CURL::gss INTERFACE_CURL_GSS_FLAVOR) + if(_gss_flavor STREQUAL "GNU") set(HAVE_GSSGNU 1) elseif(GSS_VERSION) # MIT set(CURL_KRB5_VERSION "\"${GSS_VERSION}\"") @@ -1653,7 +1670,9 @@ check_function_exists("getrlimit" HAVE_GETRLIMIT) check_function_exists("setlocale" HAVE_SETLOCALE) check_function_exists("setrlimit" HAVE_SETRLIMIT) -if(NOT APPLE) +if(APPLE) + check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) +else() # 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. @@ -1664,9 +1683,17 @@ 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) + + check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h netinet/in.h sys/socket.h + check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h netinet/in.h sys/socket.h + check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) + check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) + check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) + + check_symbol_exists("memset_s" "string.h" HAVE_MEMSET_S) + if(NOT HAVE_MEMSET_S) + check_function_exists("memset_explicit" HAVE_MEMSET_EXPLICIT) + endif() endif() if(AMIGA) @@ -1677,14 +1704,6 @@ if(NOT _ssl_enabled) check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM) endif() -if(APPLE) - check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) -endif() -if(NOT WIN32) - check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h netinet/in.h sys/socket.h - check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h netinet/in.h sys/socket.h -endif() - check_symbol_exists("fsetxattr" "sys/xattr.h" HAVE_FSETXATTR) if(HAVE_FSETXATTR) curl_internal_test(HAVE_FSETXATTR_5) @@ -2044,6 +2063,7 @@ curl_add_if("NTLM" CURL_ENABLE_NTLM AND curl_add_if("TLS-SRP" USE_TLS_SRP) curl_add_if("HTTP2" USE_NGHTTP2) curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE) +curl_add_if("proxy-HTTP3" USE_PROXY_HTTP3) 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 @@ -2161,8 +2181,8 @@ if(NOT CURL_DISABLE_INSTALL) 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. + # Reading the LOCATION property on non-imported target errors out. + # Assume the user does not need this information in the .pc file. continue() endif() set(_libdirs "") @@ -2175,7 +2195,7 @@ if(NOT CURL_DISABLE_INSTALL) endif() if(_lib STREQUAL OpenSSL::SSL AND NOT HAVE_BORINGSSL) # BoringSSL does not provide openssl.pc set(_modules "openssl") - elseif(_lib STREQUAL ZLIB::ZLIB) + elseif(_lib STREQUAL ZLIB::ZLIB AND NOT ANDROID) # Android does not provide zlib.pc set(_modules "zlib") else() get_target_property(_modules "${_lib}" INTERFACE_LIBCURL_PC_MODULES) @@ -2209,6 +2229,9 @@ if(NOT CURL_DISABLE_INSTALL) list(APPEND LIBCURL_PC_LIBS_PRIVATE "${_lib}") list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}") endif() + elseif(_lib MATCHES "^-") # '-option' + 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}") @@ -2239,6 +2262,17 @@ if(NOT CURL_DISABLE_INSTALL) string(REPLACE ";" "," LIBCURL_PC_REQUIRES_PRIVATE "${LIBCURL_PC_REQUIRES_PRIVATE}") endif() if(LIBCURL_PC_LIBS_PRIVATE) + # Remove duplicates listed next to each other + set(_libs "") + set(_prev "") + foreach(_lib IN LISTS LIBCURL_PC_LIBS_PRIVATE) + if(NOT _prev STREQUAL _lib) + list(APPEND _libs "${_lib}") + set(_prev "${_lib}") + endif() + endforeach() + set(LIBCURL_PC_LIBS_PRIVATE "${_libs}") + string(REPLACE ";" " " LIBCURL_PC_LIBS_PRIVATE "${LIBCURL_PC_LIBS_PRIVATE}") endif() if(_ldflags) @@ -2374,7 +2408,7 @@ if(NOT CURL_DISABLE_INSTALL) # USE_MBEDTLS # USE_NGHTTP2 # USE_NGHTTP3 - # USE_NGTCP2 + # USE_NGTCP2 NGTCP2_CRYPTO_BACKEND # USE_OPENSSL OPENSSL_VERSION_MAJOR # USE_QUICHE # USE_RUSTLS diff --git a/Dockerfile b/Dockerfile index 67027b9402e8..369583f62fdc 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:f9c6a2fd2ddbc23e336b6257a5245e31f996953ef06cd13a59fa0a1df2d5c252 +FROM debian:bookworm-slim@sha256:96e378d7e6531ac9a15ad505478fcc2e69f371b10f5cdf87857c4b8188404716 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 && \ @@ -32,7 +32,7 @@ RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ ARG UID=1000 GID=1000 -RUN groupadd --gid $UID dev && \ +RUN groupadd --gid $GID dev && \ useradd --uid $UID --gid dev --shell /bin/bash --create-home dev USER dev:dev diff --git a/RELEASE-NOTES b/RELEASE-NOTES index fcf392d822ba..f6f67d988c01 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,307 +1,299 @@ -curl and libcurl 8.20.0 +curl and libcurl 8.21.0 - Public curl releases: 274 - Command line options: 273 + Public curl releases: 275 + Command line options: 274 curl_easy_setopt() options: 308 Public functions in libcurl: 100 - Authors: 1463 - Contributors: 3664 + Authors: 1489 + Contributors: 3731 This release includes the following changes: - 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] + o curl: named globs in output file name for upload glob references [77] + o HTTP/3: add proxy CONNECT and MASQUE CONNECT-UDP support (ngtcp2 QUIC) [53] + o http2: remove stream dependency tracking [40] + o lib: drop support for CURLAUTH_DIGEST_IE [4] + o libssh: add support for SHA256 host public keys [57] + o tool_urlglob: add named globs [92] This release includes the following bugfixes: - 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: drop redundant `result` check [291] - o asyn-thrdd: fix clang-tidy unused value warning [125] - o async-ares: fix query counter handling [195] - 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: adjust/add casts to fix `-Wformat-signedness` [218] - 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: fix `-Wformat-signedness` by adjusting printf masks [226] - o build: link `bcrypt.lib` via vcxproj files [239] - o build: skip detecting `pipe2()` for Apple targets [227] - o build: stop building and installing `runtests.1` and `testcurl.1` [235] - 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 clang-tidy: enable more checks, fix fallouts [254] - o cmake: add CMake Config-based dependency detection [87] - o cmake: add CMake Config-based dependency detection for c-ares, wolfSSL [134] - o cmake: do not install `wcurl` when `BUILD_CURL_EXE=OFF` [265] - o cmake: do not install shell completions when `BUILD_CURL_EXE=OFF` [263] - 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 connect: fix typo on error message - 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_RTSP_SESSION_ID.md: clarify reuse "dangers" [270] - o CURLOPT_RTSP_SESSION_ID.md: expand the comment [267] - o CURLOPT_RTSP_SESSION_ID.md: minor language fix - 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/write-out.md: tls_earlydata was adeded in 8.13.0 - o docs/cmdline-opts: tidy up retry-connrefused [190] - o docs/lib: fix typos [53] - o docs/libcurl: improve easy setopt examples [266] - o docs: clarify retry-max-time timing [294] - 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 Happy Eyeballs: add resolution time delay [238] - o haproxy: use correct ip version on client supplied address [275] - o hostip: clear the sockaddr_in6 structure before use [20] - o hostip: init the curl_jmpenv_lock appropriately [278] - o hostip: resolve user supplied ip addresses [259] - 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: clear credentials better on redirect [204] - o http: clear digest nonce on cross-orgin redirect [269] - o http: clear the proxy credentials as well on port or scheme change [246] - 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 http: use header_has_value() instead of duplicate code [251] - 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 lib1560: fix comment typo - o lib1960: fix test failure [255] - 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: fix typos in comments [240] - o lib: make resolving HTTPS DNS records reliable: [176] - o lib: minor comment typos [237] - o lib: move request specific allocations to the request struct [256] - o lib: replace `PRI*32` printf masks with C89 ones [201] - o libssh2: allocate libssh2-friendly memory in kbd_callback [225] - 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: cleanup more without care for 'initialized' [262] - o mbedtls: fix ECJPAKE matching [135] - o mbedtls: remove failf() call with first argument as NULL [249] - o md4, md5: switch to wolfCrypt API in wolfSSL builds [139] - o mime: only allow 40 levels of calls [241] - o misc: fix code quality findings [209] - o mk-ca-bundle.pl: make `ca-bundle.crt` timestamp match `certdata.txt`'s [44] - o multi: enhance pending handles fairness [284] - 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: fix unused variable warnings in !verbose builds [252] - 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 parsedate: refactor [230] - 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 pytest: check 429 handling [268] - o rand: use `BCryptGenRandom()` in UWP builds [88] - o ratelimit: reset on start [150] - o request: reset resp_trailer in new requests [186] - o runtests: skip setting ed25519 SSH key format [264] - o rustls: fix memory leak on repeated SSLKEYLOGFILE fails [280] - o rustls: handle EOF during initial handshake [203] - o schannel: increase renegotiation timeout to 60 seconds [261] - o scripts: drop redundant double-quotes: `"$var"` -> `$var` (Perl) [109] - o scripts: harden / tidy up more Perl `system()` calls [70] - o sectrust: fail on missing OCSP stapling [250] - o sendf: fix CR detection if no LF is in the chunk [219] - o setopt: clear proxy auth properties when switching [192] - o setopt: fix typos in comments [257] - o setopt: move CURLOPT_CURLU [260] - o setup connection filter: mark as setup [234] - 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 socks: use dns filter for resolving [244] - 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 test1675: unit tests for URL API helper functions [248] - 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 tests: avoid infinite recursion for `make check` [253] - o tests: use %b64[] instead of "raw" base64 [245] - 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_dirhie: fix to create drive-relative directory [276] - 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_operate: reset the upload glob counter for next URL [162] - 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 old autoreferer [236] - 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 unit1675: fix `-Wformat-signedness` [274] - 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: same origin tests [213] - o urlapi: stop extracting hostname from file:// URLs on Windows [247] - o urlapi: verify the last letter of a scheme when set explicitly [16] - o urldata.h: fix typo and lingering backtick [279] - 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: move cookiehost to struct SingleRequest [242] - o urldata: remove trailers_state [17] - o vquic: fix variable name in fallback code [207] - o vtls: fix comment typos and tidy up a type [285] - o vtls: log when key logging is enabled. [288] - o vtls_scache: check reentrancy [243] - 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 `-Wmissing-prototypes` [233] - o wolfssl: fix handling of abrupt connection close [24] - o write-out.md: minor language fix [273] - o write-out.md: tls_earlydata was adeded in 8.13.0 - o ws: fix a blocking curl_ws_send() to report written length correctly [258] - 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] + o _ENVIRONMENT.md. Windows does case insensitive env variables [214] + o _URL.md: remove the zone-id mention [227] + o AmigaOS: curl_setup.h avoid explicit_bzero with clib2 [283] + o AmigaOS: fix build fallouts, re-add to CI [279] + o asyn-thrdd: add IPv6 guards [195] + o asyn-thrdd: fix result processing without wakeup socketpair [2] + o autotools: mbedtls detection fixes [163] + o BINDINGS: Update Hollywood link [181] + o BUFQ.md: re-sync with source code [111] + o build: enable `-Wlogical-op` picky warning for GCC 4.4+ [277] + o build: omit zlib pkg-config reference for Android [130] + o cf-h2-prox: fix peer leak [132] + o cf-h2-proxy: drop interim responses [47] + o cf-https-connect: do not engage on proxy origin [236] + o cf-ip-happy.c: minor comment typo + o cf-ip-happy: update documentation [223] + o cf-socket: make Curl_addr2string static [224] + o cf-socket: set scope_id for IPv6 link-local addresses [150] + o cf-socket: store errno from do_connect in ctx->error [199] + o cfilters: fix busy loop on blocked transfers [72] + o chunked: reject invalid bytes in trailer [210] + o CIPHERS.md: fix the example that uses only TLS 1.3 [137] + o cmake/FindGSS: drop "MIT Unknown" version value, related tidy ups [292] + o cmake/FindGSS: drop CMake <3.16 compatibility logic [291] + o cmake/FindGSS: fix comment, adjust custom flavor property name [261] + o cmake/FindGSS: prioritize MIT over GNU in pkg-config detection [196] + o cmake: auto-select static nghttp2/nghttp3/ngtcp2 Config [8] + o cmake: export/forward `NGTCP2_CRYPTO_BACKEND` [99] + o cmake: fix three issues generating lib options in config files [126] + o cmake: fix zstd CMake config name [5] + o cmake: opt in `MSVC_VERSION` 1951 to picky warnings [55] + o cmake: quote `COMPONENTS` string in `curl-config.in.cmake` [80] + o cmake: simplify `LINK_ONLY` imported target extraction [294] + o config2setopts: use default protocol properly [286] + o connect: remove deref of freed pointer in trace call [128] + o content_encoding: fix limit failure message [171] + o content_encoding: fix non-last chunked rejection [209] + o content_encoding: timeout during slow decoding [170] + o cookie: check __Secure- and __Host- case sensitively when read from file [256] + o cookie: compare path case sensitively [52] + o cookie: reject control octets in file-loaded cookies [289] + o cookie: simplify strstore(), remove outdated comment [12] + o cookie: tailmatch the domains for secure override [200] + o cookie: trim trailing dots when checking PSL [39] + o creds: add sasl service name [75] + o creds: create with empty user+pass [304] + o creds: mask OAuth bearer token in trace logs [117] + o creds: remove two unused functions [158] + o curl_easy_pause.md: rephrase the stream cache when pause clause [120] + o curl_easy_setopt.md: change options when no transfer runs [122] + o curl_formdata: fix to pass long where missing, document `CURLFORM_NAMELENGTH` [243] + o curl_multi_assign.md: clarify lifetime [264] + o curl_ntlm_core: fix nettle 4+ builds in certain MultiSSL combos [87] + o curl_ntlm_core: propagate DES `CryptEncrypt()` error [84] + o curl_sha512_256: fix result code on error [166] + o CURLINFO_CONTENT_LENGTH_UPLOAD_T.md: expand [215] + o CURLMOPT_SOCKETFUNCTION.md: this sends *all* file descriptors [266] + o CURLOPT_CHUNK_BGN_FUNCTION: target is there for symlinks only [156] + o CURLOPT_DISALLOW_USERNAME_IN_URL: is for CURLOPT_URL only [61] + o CURLOPT_DOH_URL.md: does not inherit proxy options [213] + o CURLOPT_ECH.md: simplify the description language [18] + o CURLOPT_HAPROXYPROTOCOL.md: only sent for newly setup connections [32] + o CURLOPT_MAXFILESIZE: clarify this also works for on-going transfers [78] + o CURLOPT_PINNEDPUBLICKEY.md: does not apply for other origins [152] + o CURLOPT_PORT.md: use stronger language [133] + o CURLOPT_SHARE: warn about early remove [51] + o CURLOPT_SSH_HOSTKEYFUNCTION.md: for new connections only [48] + o CURLOPT_WRITEFUNCTION.md: mention redirects [226] + o CURLOPT_WRITEFUNCTION.md: remove stray reference to HSTS [208] + o delta: harden external command invocations [98] + o digest: escape control codes too [206] + o digest: flush proxy state on proxy or credential change [225] + o digest: flush state on origin or credential change [235] + o dns-httpsrr-lookup: use origin, not peer [302] + o dnscache: remove Curl_dns_entry_link [160] + o docs/libcurl: fix the version for curl_multi_socket_action + o docs: end "...can be used several times..." sentences with period [34] + o docs: fix --follow doc typo [97] + o docs: fix a couple of typos [62] + o docs: fix grammar and wording in FAQ [66] + o docs: fix odd wording in CONTRIBUTE.md [107] + o docs: note CURLOPT_PINNEDPUBLICKEY has no effect on legacy LDAP backend [65] + o docs: returned header size reflects HTTP/1-style format [203] + o doh: cap the maximum TTL to 24 hours [234] + o doh: drop redundant `curlx_dyn_free()` call in `doh_probe_done()` [232] + o doh: stricter HTTPS RNAME parsing [233] + o ECH: cleanups [20] + o event: fix wakeup consumption [93] + o ftp: avoid accessing EPSV response one byte past the NULL [9] + o ftp: remove 2 Curl_resolv_blocking() calls [30] + o ftp: remove bits.ftp_use_control_ssl [28] + o ftplistparser: clear strings.target if not symlink [148] + o gnutls: allow building with nettle 4.0 [96] + o gnutls: fix more nettle 4+ compatibility issues [94] + o gnutls: require 3.7.2 for earlydata [103] + o gsasl: fix potential double free [56] + o gtls: fix ignored return and uninitialized status in OCSP check [49] + o gtls: fix some typos [15] + o gtls: minor fixes and improvements [190] + o gtls: use the correct return code in trace output [173] + o gtls: verify OCSP response signature in gtls_verify_ocsp_status [86] + o h3-proxy: fix callback return values, and a typo in tests [139] + o hostip: remove unused MAX_HOSTCACHE_LEN and MAX_DNS_CACHE_SIZE [101] + o hsts.md: mention multiple curl invokes effect [189] + o hsts: duplicate live HSTS data in curl_easy_duphandle [183] + o http-proxy: verify CONNECT response headers [192] + o HTTP3.md: update quiche build [229] + o http: don't pass on set cookies to new origins [140] + o http: prefer chunked encoding over Content-Length: 0 [146] + o http: reject spurious CR bytes in headers [157] + o http_digest: return better error [204] + o idn: replace header guards with forward declaration [100] + o INSTALL-CMAKE.md: document CMake environment variables [246] + o INTERNALS.md: document minimum nghttp3 and ngtcp2 versions [299] + o KNOWN_BUGS.md: remove fixed GnuTLS <-> OpenSSL incompat bug [41] + o KNOWN_BUGS: remove stale Threads::Threads entry [135] + o krb5_sspi: fix error message on `DecryptMessage()` fail [269] + o ldap: base64 encode binary LDIF values with WinLDAP [273] + o ldap: fix minor leak on write callback error [24] + o ldap: fix to not leak `attribute` on OOM (WinLDAP) [79] + o ldap: switch off chasing referrals [114] + o lib678: fix to not be perma-skipped [10] + o lib: make `__STDC_VERSION__` literals `L` (where missing) + o lib: transfer origin and proxy handling [276] + o lib: two minor typos [16] + o libcurl-easy.md: minor clarifications [19] + o libssh2: do not use deprecated macros when unavailable [177] + o libssh2: drop stray double-negative from `strncmp()` result [194] + o libssh2: fix to return error code on missing parameter [198] + o libssh2: replace macro names with non-misspelled alternatives [169] + o libssh2: save non-standard port to `known_hosts` [217] + o libssh2: sync version check with INTERNALS.md [176] + o libssh2: use non-deprecated `libssh2_knownhost_addc()` [178] + o libssh: map SSH_KNOWN_HOSTS_OTHER to CURLKHMATCH_MISMATCH [125] + o m4: drop redundant conditions in TLS library detections [155] + o Makefile.am: drop test1190 listed twice [144] + o managen: apply minor fixes and improvements [115] + o mbedtls: null-terminate the private key blob [36] + o mk-unity.pl: `#include`, and not concatenate input headers [124] + o mqtt: return error on truncated Remaining Length [230] + o mqtt: validate PINGRESP and DISCONNECT have remaining_length == 0 [7] + o multi: handle pause in multi socket callback [109] + o multi: remove a stale comment [216] + o multi: silence gcc 16 `-Wnull-dereference`, bump CI job to test [54] + o multi: xfers_really_alive [288] + o netrc: remember and check filename loaded [212] + o netrc: scanner refactor [121] + o ngtcp2: fail handshake directly [138] + o openssl: do not mix OpenSSL int result with `CURLcode` variable [265] + o os400sys: fix theoretical length overflows [141] + o peer.h: fix typo in comment [202] + o pingpong: reject nul byte in server response line [268] + o progress: fix CURLINFO time reporting [145] + o psl: require libpsl 0.16.0 (2016-12-10) or greater [188] + o pytest: pass `--disable` to curl [175] + o pytest: re-enable test test_05_01 and test_05_02 for quiche 0.29.0+ [154] + o pythonlint.sh: make it fail on error, fix ruff warnings in pytest [67] + o quic: count zero length packets against max [179] + o ratelimits: use minimal burst rate [245] + o RELEASE-PROCEDURE.md: update coming relese dates + o resolve: mention in error that IP address is expected [205] + o rtsp: bump buf after rtsp_filter_rtp() [88] + o runner.pm: apply minor correctness fix [105] + o runner.pm: set `CURL_TESTNUM` for `precheck` commands [13] + o runtests: fix tests for curl builds with embedded CA bundle [187] + o rustls: error on CURLOPT_CRLFILE with native CA store [59] + o schannel: check `schannel_sha256sum()` success, and more [165] + o schannel: enforce Extended Key Usage for custom CA roots [29] + o schannel: error on TLS 1.3-only with cipher list [136] + o schannel: fix https proxy for client cert and certinfo [280] + o schannel: fix revoke_best_effort setting for proxy [70] + o schannel: use fopen instead CreateFile [191] + o schannel_verify: avoid out of blob access [11] + o schannel_verify: simplify CryptQueryObject use [159] + o scripts: catch Credits-to contributors [127] + o SECURITY-ADVISORY.md: expand [220] + o setopt: changing the proxy port is also a proxy change [23] + o setopt: clear proxy auth properly on NULL [81] + o setopt: clear the "custom" CA booleans when set to NULL [218] + o setopt: CURLOPT_MAXCONNECTS set to 0 restores default value [161] + o setopt: defref the old referer when setting a new [168] + o setopt: fix to honor `CURLOPT_PROXY_CAINFO_BLOB` over Native CA [26] + o setopt: gate a few proxy TLS options by checking backend support [35] + o setopt: more careful cleanup of the HSTS cache [45] + o setopt: return error if received `curl_blob->data` is NULL [185] + o show-headers.md: mention bold headers and --no-styled-output [17] + o sigv4: URL encode the user name in the header [193] + o smb: constify `strchr()` result variable [257] + o smb: integer overflow proof a size check [263] + o smbserver: update internal id generation for Python 3 [238] + o socket: introduce `SOCK_EAGAIN()` and use it [278] + o socket: use name `sockerr` for socket error variables [271] + o socks_sspi: invalid response length is a fatal error [272] + o socks_sspi: store socks5_gssapi_enctype [262] + o spnego_sspi: honor CURLOPT_GSSAPI_DELEGATION for Windows SSPI [89] + o spnego_sspi: preserve distinction btw policy-only and uncond delegation [74] + o src: fix comment typos [83] + o src: sync nghttp2 versions checks with current requirements [300] + o ssl native_ca_store: always reinit [211] + o SSLCERTS: document 8.19.0 default Native CA builds (Windows) [14] + o sspi: clear SSPI credentials on AcquireCredentialsHandle failure [76] + o sspi: free libcurl allocated memory with curlx_free [274] + o telnet: drop an `int` cast no longer necessary [270] + o telnet: drop redundant interim variables [275] + o telnet: fix error message typos [186] + o telnet: fix old copy-paste typo in variable name [281] + o telnet: honor CURLOPT_TIMEOUT in send_telnet_data() [104] + o test1588: use %TESTNUMBER, not hard-coded number [118] + o test1981: explicitly set the locale [85] + o tests: add `cookies` feature to some tests [182] + o tests: add an assert to avoid IPC blocking [69] + o tests: add the "--resolve" keyword to tests that lack it [184] + o tests: fix unit1636 with --disable-progress-meter [37] + o tftp: avoid the timeout calc if the timeout is crazy [151] + o tftp: stricter option name checks [90] + o tidy-up: add space around operators, where missing [147] + o tidy-up: apply clang-format fixes [153] + o tidy-up: drop stray casts for allocated pointers [174] + o tidy-up: miscellaneous [106] + o tls: fix incomplete mTLS config in conn reuse and session cache [108] + o tls: wolfssl: fixes for PQC key shares [239] + o tool: warn when --ssl and --ftp-ssl-control override each other [129] + o tool_formparse.c: fix two minor comment typos [25] + o tool_formparse: polish error message + make two functions static [1] + o tool_formparse: tool2curlparts is no longer recursive [33] + o tool_help: rectify a bad assert [143] + o tool_operhlp: avoid NULL to %s [142] + o tool_urlglob: avoid overflow at end of range [22] + o tool_urlglob: better 'Duplicate glob name' position [82] + o tool_urlglob: make globbing error reported for correct position [91] + o tool_writeout: fix %time{} output for %s [231] + o transfer: clear referer when set to NULL [112] + o unit1675: fix potential memory leak on dynbuf fail path [197] + o unix-sockets: ignore proxy settings [6] + o URL-SYNTAX: document more URL parsing details [134] + o url: compare full origin when setting credentials [42] + o url: connection credentials origin [228] + o url: connection reuse fixes for starttls [68] + o url: detect proxy changes read from environment [110] + o url: don't log bits.close state [290] + o url: fix connection reuse for starttls protocols [27] + o url: keep the question mark for empty queries [73] + o url: remove superfluous check [131] + o url: url_match_destination fix [43] + o urlapi: accept 0X prefix in IPv4 address as well [63] + o urlapi: change more lowercase percent-encoded to uppercase [71] + o urlapi: compare zone-id in Curl_url_same_origin() [95] + o urlapi: consume trailing dots after IPv4 numerical addresses [50] + o urlapi: deny hostnames with more than one trailing dot [58] + o urlapi: drop base fragment on empty redirect [64] + o urlapi: fix an issue parsing file URLs [149] + o urlapi: fix memleaks on error in `parse_hostname_login()` [221] + o urlapi: fix redirect handling if CURLU_NO_GUESS_SCHEME is set [46] + o urlapi: forbid '|' in host [172] + o urlapi: handle redirect without set scheme with default-scheme [38] + o urlapi: URL decode hostname before IP address normalization [207] + o user-agent.md: mention double quotes too [3] + o var: use a dedicated pointer for the alloc [219] + o verify-release: verify more thoroughly with git [249] + o vquic: drop stray casts for `iovec.iov_len` [162] + o vquic: fix `-Wunused-parameter` with proxies disabled [260] + o vtls: more large buffer support and error checks for SHA-256 [164] + o vtls: use Curl_safecmp for CRLfile and pinned_key comparison [116] + o vtls_scache: include signature_algorithms in the SSL peer cache key [123] + o vtls_spack: drop redundant macro fallbacks [167] + o VULN-DISCLOSURE-POLICY.md: emphasize comm as a human [180] + o VULN-DISCLOSURE-POLICY.md: emphasize the no email thank you part [113] + o VULN-DISCLOSURE-POLICY.md: test code is not secure [119] + o VULN-DISCLOSURE-POLICY: non-released code [253] + o websockets: auto-tunnel through http proxy [102] + o websockets: buffer ugprade data at connection level [237] + o windows: update MS SDK versions in comments [60] + o winldap: avoid NULL pointer deref on `ldap_get_dn()` fail [242] + o ws: make pong sending lazy [201] + o x509asn1: fix DH public key parameter extraction [44] + o x509asn1: fix operator order in do_pubkey [21] This release includes the following known bugs: @@ -323,306 +315,310 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Alex Hamilton, am-perip on hackerone, Arkadi Vainbrand, bird on github, - BlackFuffey on github, Carlos Carrillo, Carlos Henrique Lima Melara, - crawfordxx, Cutiapreta on hackerone, Dag-Erling Smørgrav, Dan Arnfield, - Dan Fandrich, Daniel McCarney, Daniel Schulte, Daniel Stenberg, - dependabot[bot], Dexter Gerig, Dio Putra, Dwij Mehta, Ercan Ermis, - fds242 on github, finkjsc on github, Fiona Klute, Flavio Amieiro, - Geeknik Labs, Greg Kroah-Hartman, Harry Sintonen, Henrique Pereira, - herbenderbler on github, Ian Spence, Izan on hackerone, James Fuller, - Jason Stangroome, John Haugabook, Juan Belón, 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, Muhamad Arga Reksapati, Ngoc Hieu, nitrogene on github, - Orgad Shaneh, Osama Hamad, Otis Cui Lei, Patrick Monnerat, Quac Tran, - Ray Satiro, renovate[bot], Richard Tollerton, Rob Crittenden, - Samuel Henrique, Scott Boudreaux, Sergey Fedorov, sergio-nsk on github, - Stefan Eissing, Ted Lyngmo, Terrance Wong, Tim Omta, Viktor Szakats, - Vladimír Marek, xkilua on hackerone, Yalguun Tumenkhuu, Yedaya Katsman, - Yiwei Hou, Yoshiro Yoneya - (73 contributors) + 0xN3R3K3, 11soda11, Ady Elouej, A Johnston, Alan De Smet, alhudz, + alienowo on hackerone, ambikeesshh, amitbidlan, Andreas Falkenhahn, + Andrei Rybak, Andrew Nesbitt, Aritra Basu, av223119 on github, + azraelxuemo on hackerone, Bartel Sielski, Bastian Jesuiter, + BazaarAcc32 on github, Bill Mill, Bryan Henderson, ByteRay on hackerone, + chrizilla on github, co-authors in libssh2, correctmost on github, + Dan Fandrich, Daniel Gustafsson, Daniel Stenberg, Dario Vinella, + Darren Banfi, Dave Walker, daviey on hackerone, dependabot[bot], + dyingc on github, Earnestly on github, Elise Vance, Emanuel Krollmann, + Eunsoo Kim, evergarden1123 on hackerone, Fabian Keil, Filipe Casal, + Gao Liyou, Guancheng Li, Guannan Wang, Harry Sintonen, Hem Parekh, htasta, + jeffhuang, Jeremy Nicoll, Jiashuo Liang, jjchuck on hackerone, + Johannes Schlatow, Josef Cejka, Joshua Rogers, Kai Pastor, Marcel Raad, + Mark Esler, Max Dymond, Michael Kaufmann, mik, Mike-menny on github, + Muhamad Arga Reksapati, mulan_dh on hackerone, netspacer.research, + oreadvanthink on github, parasol-aser, penpal, Peter Krefting, Philip H., + Rainer Jung, Randall S. Becker, Raymond Steen, Ray Satiro, + renjian on hackerone, renovate[bot], Ross Burton, Saud Alshareef, + Sergio Correia, sfan5 on github, Shintomon Mathew, sideshowbarker on github, + Sollace on github, Song X. Gao, sourceturner, Stefan Eissing, + Tatsuhiro Tsujikawa, Tim Martin, tiymat, Tobias Frauenschläger, + Trail of Bits, Vasiliy-Kkk, vectorqueue on hackerone, vegagent on hackerone, + Viktor Szakats, violet12331 on hackerone, Will Cosgrove, + wulin-nudt on github, Xi Ruoyao, x-xiang on github, Yedaya Katsman, + Zartaj Majeed, zhanhb on github, Zhanpeng Liu + (102 contributors) References to bug reports and discussions on issues: - [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 - [162] = https://curl.se/bug/?i=21402 - [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 - [192] = https://curl.se/bug/?i=21453 - [193] = https://curl.se/bug/?i=21290 - [194] = https://curl.se/bug/?i=21174 - [195] = https://curl.se/bug/?i=21399 - [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 - [204] = https://curl.se/bug/?i=21345 - [206] = https://curl.se/bug/?i=21231 - [207] = https://curl.se/bug/?i=21281 - [208] = https://curl.se/bug/?i=20715 - [209] = https://curl.se/bug/?i=21393 - [210] = https://curl.se/bug/?i=21235 - [211] = https://curl.se/bug/?i=21232 - [212] = https://curl.se/bug/?i=21230 - [213] = https://curl.se/bug/?i=21328 - [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 - [218] = https://curl.se/bug/?i=21339 - [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 - [225] = https://curl.se/bug/?i=21336 - [226] = https://curl.se/bug/?i=21335 - [227] = https://curl.se/bug/?i=21236 - [228] = https://curl.se/bug/?i=21215 - [229] = https://curl.se/bug/?i=21267 - [230] = https://curl.se/bug/?i=21394 - [231] = https://curl.se/bug/?i=21222 - [232] = https://curl.se/bug/?i=21266 - [233] = https://curl.se/bug/?i=21392 - [234] = https://curl.se/bug/?i=21437 - [235] = https://curl.se/bug/?i=21461 - [236] = https://curl.se/bug/?i=21322 - [237] = https://curl.se/bug/?i=21388 - [238] = https://curl.se/bug/?i=21354 - [239] = https://curl.se/bug/?i=21386 - [240] = https://curl.se/bug/?i=21385 - [241] = https://curl.se/bug/?i=21384 - [242] = https://curl.se/bug/?i=21312 - [243] = https://curl.se/bug/?i=21383 - [244] = https://curl.se/bug/?i=21297 - [245] = https://curl.se/bug/?i=21313 - [246] = https://curl.se/bug/?i=21304 - [247] = https://curl.se/bug/?i=21296 - [248] = https://curl.se/bug/?i=21296 - [249] = https://curl.se/bug/?i=21441 - [250] = https://curl.se/bug/?i=21444 - [251] = https://curl.se/bug/?i=21302 - [252] = https://curl.se/bug/?i=21380 - [253] = https://curl.se/bug/?i=21378 - [254] = https://curl.se/bug/?i=20794 - [255] = https://curl.se/bug/?i=21377 - [256] = https://curl.se/bug/?i=21301 - [257] = https://curl.se/bug/?i=21303 - [258] = https://curl.se/bug/?i=21372 - [259] = https://curl.se/bug/?i=21146 - [260] = https://curl.se/bug/?i=21298 - [261] = https://curl.se/bug/?i=21270 - [262] = https://curl.se/bug/?i=21440 - [263] = https://curl.se/bug/?i=21460 - [264] = https://curl.se/bug/?i=21360 - [265] = https://curl.se/bug/?i=21458 - [266] = https://curl.se/bug/?i=21364 - [267] = https://curl.se/bug/?i=21363 - [268] = https://curl.se/bug/?i=21357 - [269] = https://curl.se/bug/?i=21359 - [270] = https://curl.se/bug/?i=21358 - [273] = https://curl.se/bug/?i=21455 - [274] = https://curl.se/bug/?i=21351 - [275] = https://curl.se/bug/?i=21340 - [276] = https://curl.se/bug/?i=21449 - [278] = https://curl.se/bug/?i=21432 - [279] = https://curl.se/bug/?i=21430 - [280] = https://curl.se/bug/?i=21427 - [284] = https://curl.se/bug/?i=21396 - [285] = https://curl.se/bug/?i=21421 - [288] = https://curl.se/bug/?i=19814 - [291] = https://curl.se/bug/?i=21415 - [294] = https://curl.se/bug/?i=21411 + [1] = https://curl.se/bug/?i=21510 + [2] = https://curl.se/bug/?i=21476 + [3] = https://curl.se/mail/archive-2026-04/0029.html + [4] = https://curl.se/bug/?i=21486 + [5] = https://curl.se/bug/?i=21538 + [6] = https://curl.se/bug/?i=21630 + [7] = https://hackerone.com/reports/3702718 + [8] = https://curl.se/bug/?i=21470 + [9] = https://curl.se/bug/?i=21545 + [10] = https://curl.se/bug/?i=21641 + [11] = https://curl.se/bug/?i=21543 + [12] = https://curl.se/bug/?i=21541 + [13] = https://curl.se/bug/?i=21640 + [14] = https://curl.se/bug/?i=21634 + [15] = https://curl.se/bug/?i=21498 + [16] = https://curl.se/bug/?i=21496 + [17] = https://curl.se/bug/?i=21495 + [18] = https://curl.se/bug/?i=21536 + [19] = https://curl.se/bug/?i=21491 + [20] = https://curl.se/bug/?i=21532 + [21] = https://curl.se/bug/?i=21533 + [22] = https://curl.se/bug/?i=21529 + [23] = https://curl.se/bug/?i=21485 + [24] = https://curl.se/bug/?i=21530 + [25] = https://curl.se/bug/?i=21480 + [26] = https://curl.se/bug/?i=21631 + [27] = https://curl.se/bug/?i=21522 + [28] = https://curl.se/bug/?i=21521 + [29] = https://curl.se/bug/?i=21629 + [30] = https://curl.se/bug/?i=21512 + [32] = https://curl.se/bug/?i=21517 + [33] = https://curl.se/bug/?i=21518 + [34] = https://curl.se/bug/?i=21644 + [35] = https://curl.se/bug/?i=21514 + [36] = https://curl.se/bug/?i=21515 + [37] = https://curl.se/bug/?i=21500 + [38] = https://curl.se/bug/?i=21632 + [39] = https://curl.se/bug/?i=21636 + [40] = https://curl.se/bug/?i=21723 + [41] = https://curl.se/bug/?i=21720 + [42] = https://curl.se/bug/?i=21575 + [43] = https://curl.se/bug/?i=21573 + [44] = https://curl.se/bug/?i=21595 + [45] = https://curl.se/bug/?i=21615 + [46] = https://curl.se/bug/?i=21721 + [47] = https://curl.se/bug/?i=21626 + [48] = https://curl.se/bug/?i=21606 + [49] = https://curl.se/bug/?i=21679 + [50] = https://curl.se/bug/?i=21635 + [51] = https://curl.se/bug/?i=21633 + [52] = https://curl.se/bug/?i=21616 + [53] = https://curl.se/bug/?i=21153 + [54] = https://curl.se/bug/?i=21707 + [55] = https://curl.se/bug/?i=21714 + [56] = https://curl.se/bug/?i=21609 + [57] = https://curl.se/bug/?i=21605 + [58] = https://curl.se/bug/?i=21622 + [59] = https://curl.se/bug/?i=21614 + [60] = https://curl.se/bug/?i=21621 + [61] = https://curl.se/bug/?i=21890 + [62] = https://curl.se/bug/?i=21617 + [63] = https://curl.se/bug/?i=21820 + [64] = https://curl.se/bug/?i=21745 + [65] = https://curl.se/bug/?i=21682 + [66] = https://curl.se/bug/?i=21593 + [67] = https://curl.se/bug/?i=21597 + [68] = https://curl.se/bug/?i=21665 + [69] = https://curl.se/bug/?i=21688 + [70] = https://curl.se/bug/?i=21683 + [71] = https://curl.se/bug/?i=21592 + [72] = https://curl.se/bug/?i=21671 + [73] = https://curl.se/bug/?i=21544 + [74] = https://curl.se/bug/?i=21583 + [75] = https://curl.se/bug/?i=21585 + [76] = https://curl.se/bug/?i=21642 + [77] = https://curl.se/bug/?i=21407 + [78] = https://curl.se/bug/?i=21582 + [79] = https://curl.se/bug/?i=21576 + [80] = https://curl.se/bug/?i=21699 + [81] = https://curl.se/bug/?i=21696 + [82] = https://curl.se/bug/?i=21567 + [83] = https://curl.se/bug/?i=21570 + [84] = https://curl.se/bug/?i=21569 + [85] = https://curl.se/bug/?i=21749 + [86] = https://curl.se/bug/?i=21677 + [87] = https://curl.se/bug/?i=21562 + [88] = https://curl.se/bug/?i=21563 + [89] = https://curl.se/bug/?i=21528 + [90] = https://curl.se/bug/?i=21560 + [91] = https://curl.se/bug/?i=21561 + [92] = https://curl.se/bug/?i=21409 + [93] = https://curl.se/bug/?i=21547 + [94] = https://curl.se/bug/?i=21557 + [95] = https://curl.se/bug/?i=21686 + [96] = https://curl.se/bug/?i=21169 + [97] = https://curl.se/bug/?i=21553 + [98] = https://curl.se/bug/?i=21104 + [99] = https://curl.se/bug/?i=21523 + [100] = https://curl.se/bug/?i=21551 + [101] = https://curl.se/bug/?i=21550 + [102] = https://curl.se/bug/?i=21663 + [103] = https://curl.se/bug/?i=21750 + [104] = https://curl.se/bug/?i=21685 + [105] = https://curl.se/bug/?i=21672 + [106] = https://curl.se/bug/?i=21646 + [107] = https://curl.se/bug/?i=21705 + [108] = https://curl.se/bug/?i=21667 + [109] = https://curl.se/bug/?i=21748 + [110] = https://curl.se/bug/?i=21666 + [111] = https://curl.se/bug/?i=21678 + [112] = https://curl.se/bug/?i=21741 + [113] = https://curl.se/bug/?i=21747 + [114] = https://curl.se/bug/?i=21732 + [115] = https://curl.se/bug/?i=21670 + [116] = https://curl.se/bug/?i=21668 + [117] = https://curl.se/bug/?i=21659 + [118] = https://curl.se/bug/?i=21662 + [119] = https://curl.se/bug/?i=21660 + [120] = https://curl.se/bug/?i=21658 + [121] = https://curl.se/bug/?i=21624 + [122] = https://curl.se/bug/?i=21604 + [123] = https://curl.se/bug/?i=21651 + [124] = https://curl.se/bug/?i=21656 + [125] = https://curl.se/bug/?i=21724 + [126] = https://curl.se/bug/?i=21654 + [127] = https://curl.se/bug/?i=21653 + [128] = https://curl.se/bug/?i=21649 + [129] = https://curl.se/bug/?i=21887 + [130] = https://curl.se/bug/?i=21647 + [131] = https://curl.se/bug/?i=21650 + [132] = https://curl.se/bug/?i=21602 + [133] = https://curl.se/bug/?i=21886 + [134] = https://curl.se/bug/?i=21841 + [135] = https://curl.se/bug/?i=21734 + [136] = https://curl.se/bug/?i=21702 + [137] = https://curl.se/bug/?i=21719 + [138] = https://curl.se/bug/?i=21712 + [139] = https://curl.se/bug/?i=21802 + [140] = https://curl.se/bug/?i=21794 + [141] = https://curl.se/bug/?i=21840 + [142] = https://curl.se/bug/?i=21836 + [143] = https://curl.se/bug/?i=21837 + [144] = https://curl.se/bug/?i=21839 + [145] = https://curl.se/bug/?i=21828 + [146] = https://curl.se/bug/?i=21706 + [147] = https://curl.se/bug/?i=21793 + [148] = https://curl.se/bug/?i=21884 + [149] = https://curl.se/bug/?i=21743 + [150] = https://curl.se/bug/?i=21669 + [151] = https://curl.se/bug/?i=21782 + [152] = https://curl.se/bug/?i=21885 + [153] = https://curl.se/bug/?i=21786 + [154] = https://curl.se/bug/?i=21784 + [155] = https://curl.se/bug/?i=21781 + [156] = https://curl.se/bug/?i=21883 + [157] = https://curl.se/bug/?i=21882 + [158] = https://curl.se/bug/?i=21776 + [159] = https://curl.se/bug/?i=21760 + [160] = https://curl.se/bug/?i=21774 + [161] = https://curl.se/bug/?i=21829 + [162] = https://curl.se/bug/?i=21877 + [163] = https://curl.se/bug/?i=21727 + [164] = https://curl.se/bug/?i=21771 + [165] = https://curl.se/bug/?i=21739 + [166] = https://curl.se/bug/?i=21767 + [167] = https://curl.se/bug/?i=21768 + [168] = https://curl.se/bug/?i=21826 + [169] = https://curl.se/bug/?i=21876 + [170] = https://curl.se/bug/?i=21603 + [171] = https://curl.se/bug/?i=21756 + [172] = https://curl.se/bug/?i=21762 + [173] = https://curl.se/bug/?i=21766 + [174] = https://curl.se/bug/?i=21865 + [175] = https://curl.se/bug/?i=21816 + [176] = https://curl.se/bug/?i=21868 + [177] = https://curl.se/bug/?i=21867 + [178] = https://curl.se/bug/?i=21866 + [179] = https://curl.se/bug/?i=21869 + [180] = https://curl.se/bug/?i=21870 + [181] = https://curl.se/bug/?i=21862 + [182] = https://curl.se/bug/?i=21858 + [183] = https://curl.se/bug/?i=21809 + [184] = https://curl.se/bug/?i=21930 + [185] = https://curl.se/bug/?i=22129 + [186] = https://curl.se/bug/?i=21976 + [187] = https://curl.se/bug/?i=21970 + [188] = https://curl.se/bug/?i=21933 + [189] = https://curl.se/bug/?i=21851 + [190] = https://curl.se/bug/?i=21850 + [191] = https://curl.se/bug/?i=21773 + [192] = https://curl.se/bug/?i=21927 + [193] = https://curl.se/bug/?i=21923 + [194] = https://curl.se/bug/?i=22126 + [195] = https://curl.se/bug/?i=21881 + [196] = https://curl.se/bug/?i=22052 + [197] = https://curl.se/bug/?i=21922 + [198] = https://curl.se/bug/?i=22125 + [199] = https://curl.se/bug/?i=21914 + [200] = https://curl.se/bug/?i=21910 + [201] = https://curl.se/bug/?i=21911 + [202] = https://curl.se/bug/?i=21920 + [203] = https://curl.se/bug/?i=21912 + [204] = https://curl.se/bug/?i=21969 + [205] = https://curl.se/bug/?i=21913 + [206] = https://curl.se/bug/?i=21915 + [207] = https://curl.se/bug/?i=21918 + [208] = https://curl.se/bug/?i=21968 + [209] = https://curl.se/bug/?i=21966 + [210] = https://curl.se/bug/?i=21896 + [211] = https://curl.se/bug/?i=21902 + [212] = https://curl.se/bug/?i=21903 + [213] = https://curl.se/bug/?i=21904 + [214] = https://curl.se/bug/?i=21907 + [215] = https://curl.se/bug/?i=21953 + [216] = https://curl.se/bug/?i=21961 + [217] = https://curl.se/bug/?i=21863 + [218] = https://curl.se/bug/?i=21901 + [219] = https://curl.se/bug/?i=21898 + [220] = https://curl.se/bug/?i=21964 + [221] = https://curl.se/bug/?i=21879 + [223] = https://curl.se/bug/?i=21957 + [224] = https://curl.se/bug/?i=21946 + [225] = https://curl.se/bug/?i=21951 + [226] = https://curl.se/bug/?i=21945 + [227] = https://curl.se/bug/?i=22048 + [228] = https://curl.se/bug/?i=22040 + [229] = https://curl.se/bug/?i=22105 + [230] = https://curl.se/bug/?i=21949 + [231] = https://curl.se/bug/?i=22038 + [232] = https://curl.se/bug/?i=22133 + [233] = https://curl.se/bug/?i=22124 + [234] = https://curl.se/bug/?i=22122 + [235] = https://curl.se/bug/?i=21944 + [236] = https://curl.se/bug/?i=22033 + [237] = https://curl.se/bug/?i=22107 + [238] = https://curl.se/bug/?i=21937 + [239] = https://curl.se/bug/?i=22030 + [242] = https://curl.se/bug/?i=22000 + [243] = https://curl.se/bug/?i=22017 + [245] = https://curl.se/bug/?i=22016 + [246] = https://curl.se/bug/?i=22114 + [249] = https://curl.se/bug/?i=22018 + [253] = https://curl.se/bug/?i=22025 + [256] = https://curl.se/bug/?i=22085 + [257] = https://curl.se/bug/?i=22094 + [260] = https://curl.se/bug/?i=22104 + [261] = https://curl.se/bug/?i=22013 + [262] = https://curl.se/bug/?i=22004 + [263] = https://curl.se/bug/?i=22001 + [264] = https://curl.se/bug/?i=22088 + [265] = https://curl.se/bug/?i=22087 + [266] = https://curl.se/bug/?i=22081 + [268] = https://curl.se/bug/?i=21996 + [269] = https://curl.se/bug/?i=22003 + [270] = https://curl.se/bug/?i=22002 + [271] = https://curl.se/bug/?i=21998 + [272] = https://curl.se/bug/?i=21999 + [273] = https://curl.se/bug/?i=21926 + [274] = https://curl.se/bug/?i=21990 + [275] = https://curl.se/bug/?i=21995 + [276] = https://curl.se/bug/?i=21967 + [277] = https://curl.se/bug/?i=21893 + [278] = https://curl.se/bug/?i=21992 + [279] = https://curl.se/bug/?i=21993 + [280] = https://curl.se/bug/?i=21986 + [281] = https://curl.se/bug/?i=21979 + [283] = https://curl.se/bug/?i=21989 + [286] = https://curl.se/bug/?i=21983 + [288] = https://curl.se/bug/?i=22050 + [289] = https://curl.se/bug/?i=22070 + [290] = https://curl.se/bug/?i=22073 + [291] = https://curl.se/bug/?i=22072 + [292] = https://curl.se/bug/?i=22052 + [294] = https://curl.se/bug/?i=22063 + [299] = https://curl.se/bug/?i=22062 + [300] = https://curl.se/bug/?i=22061 + [302] = https://curl.se/bug/?i=22059 + [304] = https://curl.se/bug/?i=21943 diff --git a/REUSE.toml b/REUSE.toml index c2e8b7928889..9e6d2d166b44 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -2,8 +2,8 @@ # 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 -# uncommentable. Unless this is the case, a file should be annotated directly. +# 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,6 +13,7 @@ SPDX-PackageDownloadLocation = "https://curl.se/" [[annotations]] path = [ + ".github/pull_request_template.md", "docs/INSTALL", "docs/libcurl/symbols-in-versions", "docs/options-in-versions", diff --git a/acinclude.m4 b/acinclude.m4 index d98d4b1bef5e..ed8b0499dd6a 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -25,11 +25,11 @@ dnl CURL_CHECK_DEF (SYMBOL, [INCLUDES], [SILENT]) dnl ------------------------------------------------- dnl Use the C preprocessor to find out if the given object-style symbol -dnl is defined and get its expansion. This macro will not use default -dnl includes even if no INCLUDES argument is given. This macro will run +dnl is defined and get its expansion. This macro does not use default +dnl includes even if no INCLUDES argument is given. This macro runs dnl silently when invoked with three arguments. If the expansion would -dnl result in a set of double-quoted strings the returned expansion will -dnl actually be a single double-quoted string concatenating all them. +dnl result in a set of double-quoted strings the returned expansion is +dnl actually a single double-quoted string concatenating all them. AC_DEFUN([CURL_CHECK_DEF], [ AC_REQUIRE([CURL_CPP_P]) @@ -79,9 +79,9 @@ AC_DEFUN([CURL_CHECK_DEF], [ dnl CURL_CHECK_DEF_CC (SYMBOL, [INCLUDES], [SILENT]) dnl ------------------------------------------------- dnl Use the C compiler to find out only if the given symbol is defined -dnl or not, this can not find out its expansion. This macro will not use +dnl or not, this can not find out its expansion. This macro does not use dnl default includes even if no INCLUDES argument is given. This macro -dnl will run silently when invoked with three arguments. +dnl runs silently when invoked with three arguments. AC_DEFUN([CURL_CHECK_DEF_CC], [ AS_VAR_PUSHDEF([ac_HaveDef], [curl_cv_have_def_$1]) @@ -860,7 +860,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ case X-"$curl_cv_gclk_LIBS" in X-unknown) AC_MSG_RESULT([cannot find clock_gettime]) - AC_MSG_WARN([HAVE_CLOCK_GETTIME_MONOTONIC will not be defined]) + AC_MSG_WARN([HAVE_CLOCK_GETTIME_MONOTONIC is not defined]) curl_func_clock_gettime="no" ;; X-) @@ -869,7 +869,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ ;; *) 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]) + AC_MSG_WARN([needs -lrt but asked not to use it, HAVE_CLOCK_GETTIME_MONOTONIC is not defined]) curl_func_clock_gettime="no" else if test -z "$curl_cv_save_LIBS"; then @@ -908,7 +908,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [ AC_MSG_RESULT([yes]) ],[ AC_MSG_RESULT([no]) - AC_MSG_WARN([HAVE_CLOCK_GETTIME_MONOTONIC will not be defined]) + AC_MSG_WARN([HAVE_CLOCK_GETTIME_MONOTONIC is not defined]) curl_func_clock_gettime="no" LIBS="$curl_cv_save_LIBS" ]) @@ -1028,7 +1028,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SELECT], [ dnl CURL_VERIFY_RUNTIMELIBS dnl ------------------------------------------------- dnl Verify that the shared libs found so far can be used when running -dnl programs, since otherwise the situation will create odd configure errors +dnl programs, since otherwise the situation creates odd configure errors dnl that are misleading people. dnl dnl Make sure this test is run BEFORE the first test in the script that @@ -1040,7 +1040,7 @@ AC_DEFUN([CURL_VERIFY_RUNTIMELIBS], [ dnl this test is of course not sensible if we are cross-compiling! if test "$cross_compiling" != "yes"; then - dnl just run a program to verify that the libs checked for previous to this + dnl run a program to verify that the libs checked for previous to this dnl point also is available runtime! AC_MSG_CHECKING([runtime libs availability]) CURL_RUN_IFELSE([ @@ -1063,7 +1063,7 @@ dnl CURL_CHECK_CA_BUNDLE dnl ------------------------------------------------- dnl Check if a default ca-bundle should be used dnl -dnl regarding the paths this will scan: +dnl regarding the paths this scans: dnl /etc/ssl/certs/ca-certificates.crt Debian systems dnl /etc/pki/tls/certs/ca-bundle.crt Redhat and Mandriva dnl /usr/share/ssl/certs/ca-bundle.crt old(er) Redhat @@ -1127,7 +1127,7 @@ AS_HELP_STRING([--without-ca-path], [Do not use a default CA path]), capath="$want_capath" ca="no" elif test "$ca_native" != "no"; then - dnl native ca configured, do not look further + dnl native CA configured, do not look further ca="no" capath="no" else @@ -1161,7 +1161,7 @@ AS_HELP_STRING([--without-ca-path], [Do not use a default CA path]), fi done fi - AC_MSG_NOTICE([want $want_capath ca $ca]) + AC_MSG_NOTICE([want $want_capath CA $ca]) if test "x$want_capath" = "xunset"; then check_capath="/etc/ssl/certs" fi @@ -1197,13 +1197,13 @@ AS_HELP_STRING([--without-ca-path], [Do not use a default CA path]), if test "x$ca" != "xno"; then CURL_CA_BUNDLE="$ca" - AC_DEFINE_UNQUOTED(CURL_CA_BUNDLE, "$ca", [Location of default ca bundle]) + AC_DEFINE_UNQUOTED(CURL_CA_BUNDLE, "$ca", [Location of default CA bundle]) AC_SUBST(CURL_CA_BUNDLE) AC_MSG_RESULT([$ca]) fi if test "x$capath" != "xno"; then CURL_CA_PATH="\"$capath\"" - AC_DEFINE_UNQUOTED(CURL_CA_PATH, "$capath", [Location of default ca path]) + AC_DEFINE_UNQUOTED(CURL_CA_PATH, "$capath", [Location of default CA path]) AC_MSG_RESULT([$capath (capath)]) fi if test "x$ca" = "xno" && test "x$capath" = "xno"; then @@ -1258,7 +1258,7 @@ AS_HELP_STRING([--without-ca-embed], [Do not embed a default CA bundle in the cu AC_MSG_RESULT([$want_ca_embed]) else AC_MSG_RESULT([no]) - AC_MSG_WARN([perl was not found. Will not do CA embed.]) + AC_MSG_WARN([perl was not found. Cannot do CA embed.]) fi else AC_MSG_RESULT([no]) @@ -1441,15 +1441,13 @@ AC_DEFUN([CURL_PREPARE_BUILDINFO], [ *-*-*bsd*) curl_pflags="${curl_pflags} BSD";; esac - case $host in - *-*-android*) - curl_pflags="${curl_pflags} ANDROID" - ANDROID_PLATFORM_LEVEL=`echo "$host_os" | $SED -ne 's/.*android\(@<:@0-9@:>@*\).*/\1/p'` - if test -n "${ANDROID_PLATFORM_LEVEL}"; then - curl_pflags="${curl_pflags}-${ANDROID_PLATFORM_LEVEL}" - fi - ;; - esac + if test "$curl_cv_android" = "yes"; then + curl_pflags="${curl_pflags} ANDROID" + ANDROID_PLATFORM_LEVEL=`echo "$host_os" | $SED -ne 's/.*android\(@<:@0-9@:>@*\).*/\1/p'` + if test -n "${ANDROID_PLATFORM_LEVEL}"; then + curl_pflags="${curl_pflags}-${ANDROID_PLATFORM_LEVEL}" + fi + fi if test "$curl_cv_native_windows" = "yes"; then curl_pflags="${curl_pflags} WIN32" fi @@ -1498,7 +1496,7 @@ dnl CURL_CPP_P dnl dnl Check if $cpp -P should be used for extract define values due to gcc 5 dnl splitting up strings and defines between line outputs. gcc by default -dnl (without -P) will show TEST EINVAL TEST as +dnl (without -P) shows TEST EINVAL TEST as dnl dnl # 13 "conftest.c" dnl TEST diff --git a/appveyor.sh b/appveyor.sh index 56bfc8848d6c..8852caec9b46 100644 --- a/appveyor.sh +++ b/appveyor.sh @@ -39,13 +39,14 @@ if [ -n "${CMAKE_GENERATOR:-}" ]; then *) openssl_suffix='-Win64';; esac - if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2022' ]; then + if [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2026' ]; then openssl_root_win="C:/OpenSSL-v36${openssl_suffix}" - openssl_root="$(cygpath "${openssl_root_win}")" + elif [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2022' ]; then + openssl_root_win="C:/OpenSSL-v35${openssl_suffix}" elif [ "${APPVEYOR_BUILD_WORKER_IMAGE}" = 'Visual Studio 2019' ]; then openssl_root_win="C:/OpenSSL-v30${openssl_suffix}" - openssl_root="$(cygpath "${openssl_root_win}")" fi + [ -n "${openssl_root_win:-}" ] && openssl_root="$(cygpath "${openssl_root_win}")" # Install custom cmake version if [ -n "${CMAKE_VERSION:-}" ]; then @@ -58,7 +59,7 @@ if [ -n "${CMAKE_GENERATOR:-}" ]; then 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 + --location --proto-redir =https "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 diff --git a/appveyor.yml b/appveyor.yml index 899bb497c4c7..9335da3892c0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,14 +36,16 @@ environment: matrix: # CMake Visual Studio builds - - job_name: 'CM VS2022, Release, x64, OpenSSL 3.6, Shared, Build-tests' + - 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: 'CM VS2022, Release, arm64, Schannel, Static, !DEBUGBUILD, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - CMAKE_GENERATOR: 'Visual Studio 17 2022' + - job_name: 'CM VS2026, Release, arm64, Schannel, Static, !DEBUGBUILD, Build-tests' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2026' + CMAKE_VERSION: 4.2.1 + CMAKE_SHA256: dfc2b2afac257555e3b9ce375b12b2883964283a366c17fec96cf4d17e4f1677 + CMAKE_GENERATOR: 'Visual Studio 18 2026' CMAKE_GENERATE: '-A ARM64 -DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=OFF' - job_name: 'CM VS2010, Debug, x64, Schannel, Shared, Build-tests & examples' @@ -86,9 +88,11 @@ environment: 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.6 + Schannel, Static, Unicode, Build-tests & examples, clang-cl' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - CMAKE_GENERATOR: 'Visual Studio 17 2022' + - job_name: 'CM VS2026, Debug, x64, OpenSSL 3.5 + Schannel, Static, Unicode, Build-tests & examples, clang-cl' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2026' + CMAKE_VERSION: 4.2.1 + CMAKE_SHA256: dfc2b2afac257555e3b9ce375b12b2883964283a366c17fec96cf4d17e4f1677 + CMAKE_GENERATOR: 'Visual Studio 18 2026' 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' @@ -97,9 +101,11 @@ environment: ENABLE_UNICODE: 'ON' CMAKE_GENERATE: '-A x64 -DENABLE_UNICODE=ON -DENABLE_DEBUG=OFF' - - job_name: 'CM VS2022, Debug, x64, !ssl, Static, Build-tests' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' - CMAKE_GENERATOR: 'Visual Studio 17 2022' + - job_name: 'CM VS2026, Debug, x64, !ssl, Static, Build-tests' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2026' + CMAKE_VERSION: 4.2.1 + CMAKE_SHA256: dfc2b2afac257555e3b9ce375b12b2883964283a366c17fec96cf4d17e4f1677 + CMAKE_GENERATOR: 'Visual Studio 18 2026' CMAKE_GENERATE: '-A x64 -DBUILD_SHARED_LIBS=OFF -DCURL_USE_SCHANNEL=OFF' - job_name: 'CM VS2022, Debug, x64, !ssl, Static, HTTP-only, Build-tests' @@ -110,7 +116,7 @@ environment: # VisualStudioSolution builds - job_name: 'VisualStudioSolution VS2010, Release, x86, Schannel' - APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2013' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' PRJ_CFG: 'DLL Release - DLL Windows SSPI - DLL WinIDN' PLAT: 'Win32' VC_VERSION: VC10 diff --git a/configure.ac b/configure.ac index f7a92ea1b076..944858de6060 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,30 @@ CURL_CHECK_OPTION_RT CURL_CHECK_OPTION_HTTPSRR CURL_CHECK_OPTION_ECH CURL_CHECK_OPTION_SSLS_EXPORT +AC_MSG_CHECKING([whether to enable HTTP/3 proxy support]) +OPT_PROXY_HTTP3="default" +AC_ARG_ENABLE(proxy-http3, +AS_HELP_STRING([--enable-proxy-http3],[Enable HTTP/3 proxy support (experimental)]) +AS_HELP_STRING([--disable-proxy-http3],[Disable HTTP/3 proxy support (experimental)]), + OPT_PROXY_HTTP3=$enableval) +case "$OPT_PROXY_HTTP3" in + no) + want_proxy_http3="no" + curl_proxy_http3_msg="no (--enable-proxy-http3)" + AC_MSG_RESULT([no]) + ;; + default) + want_proxy_http3="no" + curl_proxy_http3_msg="no (--enable-proxy-http3)" + AC_MSG_RESULT([no]) + ;; + *) + want_proxy_http3="yes" + curl_proxy_http3_msg="enabled (--disable-proxy-http3)" + AC_MSG_RESULT([yes]) + ;; +esac +USE_PROXY_HTTP3=0 XC_CHECK_PATH_SEPARATOR @@ -204,7 +228,7 @@ exec $CC "$@" EOF dnl ********************************************************************** -dnl See which TLS backend(s) that are requested. Just do all the +dnl See which TLS backend(s) that are requested. Do all the dnl TLS AC_ARG_WITH() invokes here and do the checks later dnl ********************************************************************** OPT_SCHANNEL=no @@ -289,7 +313,7 @@ AS_HELP_STRING([--with-rustls=PATH],[where to look for Rustls, PATH points to th fi ]) -OPT_APPLE_SECTRUST=$curl_cv_apple +OPT_APPLE_SECTRUST=no AC_ARG_WITH(apple-sectrust, AS_HELP_STRING([--with-apple-sectrust],[enable Apple OS native certificate verification]),[ OPT_APPLE_SECTRUST=$withval @@ -325,6 +349,22 @@ AS_HELP_STRING([--with-test-caddy=PATH],[where to find caddy for testing]), ) AC_SUBST(CADDY) +if test -x /usr/local/bin/h2o; then + H2O=/usr/local/bin/h2o +elif test -x /usr/bin/h2o; then + H2O=/usr/bin/h2o +elif test -x "`brew --prefix 2>/dev/null`/bin/h2o"; then + H2O=`brew --prefix`/bin/h2o +fi +AC_ARG_WITH(test-h2o,dnl +AS_HELP_STRING([--with-test-h2o=PATH],[where to find h2o for testing]), + H2O=$withval + if test "x$H2O" = "xno"; then + H2O="" + fi +) +AC_SUBST(H2O) + if test -x /usr/sbin/vsftpd; then VSFTPD=/usr/sbin/vsftpd elif test -x /usr/local/sbin/vsftpd; then @@ -687,8 +727,8 @@ esac AM_CONDITIONAL(BUILD_UNITTESTS, test "$supports_unittests" = "yes") -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. +dnl In order to detect support of sendmmsg() and accept4(), we need to escape +dnl the POSIX jail by defining _GNU_SOURCE or does not expose it. case $host_os in *linux*|cygwin*|msys*|gnu*) CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE" @@ -703,8 +743,10 @@ dnl ********************************************************************** CURL_CHECK_WIN32_CRYPTO +curl_cv_android='no' curl_cv_apple='no' case $host in + *-*-android*) curl_cv_android='yes';; *-apple-*) curl_cv_apple='yes';; esac @@ -742,7 +784,7 @@ AS_HELP_STRING([--disable-unity],[Disable unity (default)]), AC_MSG_RESULT([no]) ) if test -z "$PERL" && test "$want_unity" = "yes"; then - AC_MSG_WARN([perl was not found. Will not enable unity.]) + AC_MSG_WARN([perl was not found. Cannot enable unity.]) want_unity='no' fi AM_CONDITIONAL([USE_UNITY], [test "$want_unity" = "yes"]) @@ -1159,7 +1201,7 @@ AS_HELP_STRING([--disable-docs],[Disable documentation]), BUILD_DOCS=1 ) if test -z "$PERL" && test "$BUILD_DOCS" != "0"; then - AC_MSG_WARN([perl was not found. Will not build documentation.]) + AC_MSG_WARN([perl was not found. Cannot build documentation.]) BUILD_DOCS=0 fi @@ -1521,7 +1563,10 @@ else dnl replace 'HAVE_LIBZ' in the automake makefile.ams AMFIXLIB="1" AC_MSG_NOTICE([found both libz and libz.h header]) - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE zlib" + dnl Android does not provide zlib.pc + if test "$curl_cv_android" = "no"; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE zlib" + fi curl_zlib_msg="enabled" fi fi @@ -1574,7 +1619,7 @@ if test "x$OPT_BROTLI" != "xno"; then DIR_BROTLI=`echo $LD_BROTLI | $SED -e 's/^-L//'` ;; off) - dnl no --with-brotli option given, just check default places + dnl no --with-brotli option given, check default places ;; *) dnl use the given --with-brotli spot @@ -1664,7 +1709,7 @@ if test "x$OPT_ZSTD" != "xno"; then ;; off) - dnl no --with-zstd option given, just check default places + dnl no --with-zstd option given, check default places ;; *) dnl use the given --with-zstd spot @@ -1810,7 +1855,7 @@ int main(int argc, char **argv) { #ifdef _WIN32 /* on Windows, writing to the argv does not hide the argument in - process lists so it can just be skipped */ + process lists so it can be skipped */ (void)argc; (void)argv; return 1; @@ -2092,7 +2137,7 @@ Use --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schann dnl explicitly built without TLS ;; xD*) - AC_MSG_ERROR([--without-ssl has been set together with an explicit option to use an ssl library + AC_MSG_ERROR([--without-ssl has been set together with an explicit option to use an SSL library (e.g. --with-openssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-schannel, --with-amissl, --with-rustls). Since these are conflicting parameters, verify which is the desired one and drop the other.]) ;; @@ -2401,7 +2446,7 @@ if test "x$OPT_LIBSSH2" != "xno"; then ;; off) - dnl no --with-libssh2 option given, just check default places + dnl no --with-libssh2 option given, check default places ;; *) dnl use the given --with-libssh2 spot @@ -2422,7 +2467,7 @@ if test "x$OPT_LIBSSH2" != "xno"; then CPPFLAGS="$CPPFLAGS $CPP_SSH2" LIBS="$LIB_SSH2 $LIBS" - dnl check for function added in libssh2 version 1.9.0 + dnl check for function added in libssh2 v1.9.0 AC_CHECK_LIB(ssh2, libssh2_agent_get_identity_path) AC_CHECK_HEADER(libssh2.h, @@ -2478,7 +2523,7 @@ elif test "x$OPT_LIBSSH" != "xno"; then ;; off) - dnl no --with-libssh option given, just check default places + dnl no --with-libssh option given, check default places ;; *) dnl use the given --with-libssh spot @@ -2726,7 +2771,7 @@ dnl ********************************************************************** dnl Check for linker switch for versioned symbols dnl ********************************************************************** -versioned_symbols_flavour= +versioned_symbols_flavor= AC_MSG_CHECKING([whether versioned symbols are wanted]) AC_ARG_ENABLE(versioned-symbols, AS_HELP_STRING([--enable-versioned-symbols], [Enable versioned symbols in shared library]) @@ -2745,21 +2790,21 @@ AS_HELP_STRING([--disable-versioned-symbols], [Disable versioned symbols in shar else AC_MSG_RESULT(yes) if test "x$enableval" != "xyes"; then - versioned_symbols_flavour="$enableval" + versioned_symbols_flavor="$enableval" elif test "$CURL_WITH_MULTI_SSL" = "1"; then - versioned_symbols_flavour="MULTISSL_" + versioned_symbols_flavor="MULTISSL_" elif test "$OPENSSL_ENABLED" = "1"; then - versioned_symbols_flavour="OPENSSL_" + versioned_symbols_flavor="OPENSSL_" elif test "$MBEDTLS_ENABLED" = "1"; then - versioned_symbols_flavour="MBEDTLS_" + versioned_symbols_flavor="MBEDTLS_" elif test "$WOLFSSL_ENABLED" = "1"; then - versioned_symbols_flavour="WOLFSSL_" + versioned_symbols_flavor="WOLFSSL_" elif test "$GNUTLS_ENABLED" = "1"; then - versioned_symbols_flavour="GNUTLS_" + versioned_symbols_flavor="GNUTLS_" elif test "$RUSTLS_ENABLED" = "1"; then - versioned_symbols_flavour="RUSTLS_" + versioned_symbols_flavor="RUSTLS_" else - versioned_symbols_flavour="" + versioned_symbols_flavor="" fi versioned_symbols="yes" fi @@ -2771,7 +2816,7 @@ 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_PREFIX], ["$versioned_symbols_flavor"]) 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 "$versioned_symbols" = "yes"]) @@ -3677,8 +3722,8 @@ if test "$disable_http" = "yes" || test "$USE_NGTCP" = "1"; then fi AC_ARG_WITH(quiche, -AS_HELP_STRING([--with-quiche=PATH],[Enable quiche usage]) -AS_HELP_STRING([--without-quiche],[Disable quiche usage]), +AS_HELP_STRING([--with-quiche=PATH],[Enable quiche usage (experimental)]) +AS_HELP_STRING([--without-quiche],[Disable quiche usage (experimental)]), [OPT_QUICHE=$withval]) case "$OPT_QUICHE" in no) @@ -3739,7 +3784,7 @@ if test "$want_quiche" != "no"; then AC_CHECK_LIB(quiche, quiche_conn_send_ack_eliciting, [ AC_CHECK_HEADERS(quiche.h, - experimental="$experimental HTTP3" + experimental="$experimental Quiche" AC_MSG_NOTICE([HTTP3 support is experimental]) curl_h3_msg="enabled (quiche)" AC_DEFINE(USE_QUICHE, 1, [if quiche is in use]) @@ -3882,7 +3927,7 @@ case "$OPT_ZSH_FPATH" in ;; esac if test -z "$PERL" && test -n "$ZSH_FUNCTIONS_DIR"; then - AC_MSG_WARN([perl was not found. Will not install zsh completions.]) + AC_MSG_WARN([perl was not found. Cannot install zsh completions.]) ZSH_FUNCTIONS_DIR='' fi AM_CONDITIONAL(USE_ZSH_COMPLETION, test -n "$ZSH_FUNCTIONS_DIR") @@ -3917,7 +3962,7 @@ case "$OPT_FISH_FPATH" in ;; esac if test -z "$PERL" && test -n "$FISH_FUNCTIONS_DIR"; then - AC_MSG_WARN([perl was not found. Will not install fish completions.]) + AC_MSG_WARN([perl was not found. Cannot install fish completions.]) FISH_FUNCTIONS_DIR='' fi AM_CONDITIONAL(USE_FISH_COMPLETION, test -n "$FISH_FUNCTIONS_DIR") @@ -4161,6 +4206,11 @@ if test "$curl_cv_native_windows" != "yes"; then CURL_CHECK_FUNC_STRCASECMP CURL_CHECK_FUNC_STRCMPI CURL_CHECK_FUNC_STRICMP + + CURL_CHECK_FUNC_MEMSET_S + if test "$curl_cv_func_memset_s" = "no"; then + AC_CHECK_FUNCS([memset_explicit]) + fi fi if test -z "$ssl_backends"; then @@ -5027,6 +5077,28 @@ if test "$want_ssls_export" != "no"; then fi fi +dnl ************************************************************* +dnl check whether experimental HTTP/3 proxy support is enabled +dnl +if test "$want_proxy_http3" = "yes"; then + AC_MSG_CHECKING([whether HTTP/3 proxy support is available]) + + if test "$CURL_DISABLE_PROXY" = "1"; then + AC_MSG_ERROR([--enable-proxy-http3 requires proxy support]) + elif test "$CURL_DISABLE_HTTP" = "1"; then + AC_MSG_ERROR([--enable-proxy-http3 requires HTTP support]) + elif test "$USE_NGTCP2_H3" != "1"; then + AC_MSG_ERROR([--enable-proxy-http3 requires ngtcp2 + nghttp3]) + elif test "x$OPENSSL_ENABLED" != "x1"; then + AC_MSG_ERROR([--enable-proxy-http3 currently requires OpenSSL]) + else + AC_DEFINE(USE_PROXY_HTTP3, 1, [if HTTP/3 proxy support is available]) + USE_PROXY_HTTP3=1 + AC_MSG_RESULT([yes]) + experimental="$experimental proxy-HTTP3" + fi +fi + dnl ************************************************************ dnl hiding of library internal symbols dnl @@ -5140,6 +5212,10 @@ if test "$curl_psl_msg" = "enabled"; then SUPPORT_FEATURES="$SUPPORT_FEATURES PSL" fi +if test "$USE_PROXY_HTTP3" = "1"; then + SUPPORT_FEATURES="$SUPPORT_FEATURES proxy-HTTP3" +fi + if test "$curl_gsasl_msg" = "enabled"; then SUPPORT_FEATURES="$SUPPORT_FEATURES gsasl" fi @@ -5468,11 +5544,11 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: 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} - ca fallback: ${with_ca_fallback} + CA native: ${ca_native} + CA cert bundle: ${ca}${ca_warning} + CA cert path: ${capath}${capath_warning} + CA cert embed: ${CURL_CA_EMBED_msg} + CA fallback: ${with_ca_fallback} LDAP: ${curl_ldap_msg} LDAPS: ${curl_ldaps_msg} IPFS/IPNS: ${curl_ipfs_msg} @@ -5484,6 +5560,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: HTTP1: ${curl_h1_msg} HTTP2: ${curl_h2_msg} HTTP3: ${curl_h3_msg} + proxy-HTTP3: ${curl_proxy_http3_msg} ECH: ${curl_ech_msg} HTTPS RR: ${curl_httpsrr_msg} SSLS-EXPORT: ${curl_ssls_export_msg} diff --git a/docs/BINDINGS.md b/docs/BINDINGS.md index 9a53f81f29f8..6c9aba61054b 100644 --- a/docs/BINDINGS.md +++ b/docs/BINDINGS.md @@ -61,17 +61,17 @@ 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://web.archive.org/web/20250116185836/www.hollywood-mal.com/download.html) hURL by Andreas Falkenhahn +[Hollywood](https://www.hollywood-mal.com/download.html) hURL by Andreas Falkenhahn [Java](https://github.com/covers1624/curl4j) [Julia](https://github.com/JuliaWeb/LibCURL.jl) Written by Amit Murthy -[Kapito](https://github.com/puzza007/katipo) is an Erlang HTTP library around libcurl. +[Katipo](https://github.com/puzza007/katipo) is an Erlang HTTP library around libcurl. [Lisp](https://common-lisp.net/project/cl-curl/) Written by Liam Healy -[LibQurl](https://github.com/Qriist/LibQurl) a feature rich AutoHotKey v2 (AHKv2) wrapper around libcurl. +[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 @@ -110,7 +110,7 @@ Bailiff and Bálint Szilakszi, [R](https://cran.r-project.org/package=curl) -[Rexx](https://rexxcurl.sourceforge.net/) Written Mark Hessling +[Rexx](https://rexxcurl.sourceforge.net/) Written by Mark Hessling [Ring](https://ring-lang.github.io/doc1.24/libcurl.html) RingLibCurl by Mahmoud Fayed diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index 060d3da94983..0b70bda7d9e1 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -96,10 +96,10 @@ are NULL ciphers, offering no encryption whatsoever.) ### TLS 1.2 (1.1, 1.0) cipher suites -Setting TLS 1.2 cipher suites is supported by curl with OpenSSL, LibreSSL, -BoringSSL, mbedTLS (curl 8.8.0+), wolfSSL (curl 7.53.0+). Schannel does not -support setting cipher suites directly, but does support setting algorithms -(curl 7.61.0+), see Schannel notes below. +Setting TLS 1.2 cipher suites is supported by curl with AWS-LC, BoringSSL, +LibreSSL, mbedTLS (curl 8.8.0+), OpenSSL, wolfSSL (curl 7.53.0+). Schannel +does not support setting cipher suites directly, but does support setting +algorithms (curl 7.61.0+), see Schannel notes below. For TLS 1.2 cipher suites there are multiple naming schemes, the two most used are with OpenSSL names (e.g. `ECDHE-RSA-AES128-GCM-SHA256`) and IANA names @@ -188,12 +188,13 @@ mbedTLS and wolfSSL. ```sh curl \ --tlsv1.3 \ + --tls-max 1.3 \ --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. +OpenSSL, LibreSSL, mbedTLS and wolfSSL. ```sh curl \ @@ -238,7 +239,7 @@ other keywords that tweak its operations. Applications or a system may define new alias names for priority strings that can then be used here. Since the order of items in priority strings is significant, it makes no -sense for curl to puzzle other ssl options somehow together. `--ciphers` +sense for curl to puzzle other SSL options somehow together. `--ciphers` is the single way to change priority. ### Examples @@ -269,10 +270,10 @@ 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) +- [GnuTLS Priority Strings](https://gnutls.org/manual/html_node/Priority-Strings.html) +- [IANA cipher suites list](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4) - [mbedTLS cipher suites reference](https://mbed-tls.readthedocs.io/projects/api/en/development/api/file/ssl__ciphersuites_8h/) +- [OpenSSL cipher suite names documentation](https://docs.openssl.org/master/man1/openssl-ciphers/#cipher-suite-names) - [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) +- [wolfSSL cipher support documentation](https://www.wolfssl.com/documentation/manuals/wolfssl/chapter04.html#cipher-support) diff --git a/docs/CODE_REVIEW.md b/docs/CODE_REVIEW.md index da7bb2c9d78b..a80d2eec3ce2 100644 --- a/docs/CODE_REVIEW.md +++ b/docs/CODE_REVIEW.md @@ -156,14 +156,14 @@ Maybe use of `realloc()` should rather use the dynbuf functions? Do not allow new code that grows buffers without using dynbuf. -Use of C functions that rely on a terminating zero must only be used on data -that really do have a null-terminating zero. +Use of C functions that rely on a null-terminator must only be used on data +that really do have a null-terminator (`\0` byte). ## Dangerous "data styles" -Make extra precautions and verify that memory buffers that need a terminating -zero always have exactly that. Buffers *without* a null-terminator must not be -used as input to string functions. +Make extra precautions and verify that memory buffers that need +null-terminator always have exactly that. Buffers *without* a null-terminator +must not be used as input to string functions. # Commit messages diff --git a/docs/CONTRIBUTE.md b/docs/CONTRIBUTE.md index 316809193059..f9b473c09a35 100644 --- a/docs/CONTRIBUTE.md +++ b/docs/CONTRIBUTE.md @@ -143,10 +143,10 @@ it into a pull request for you, to have the CI jobs verify it proper before it can be merged. Be prepared that some feedback on the proposed change might 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 getting deleted without action. As a submitter of a change, you are -the owner of that change until it has been merged. +As your changes are reviewed and discussed, you are expected to address any +flaws pointed out and update accordingly. Otherwise your changes risk stalling +and eventually being 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 @@ -261,7 +261,7 @@ work. 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. diff --git a/docs/CURLDOWN.md b/docs/CURLDOWN.md index ce19b5f5d606..c804eae746b2 100644 --- a/docs/CURLDOWN.md +++ b/docs/CURLDOWN.md @@ -97,7 +97,7 @@ option. The available TLS backends are: - `GnuTLS` - `mbedTLS` -- `OpenSSL` (also covers BoringSSL, LibreSSL, quictls, AWS-LC and AmiSSL) +- `OpenSSL` (also covers AmiSSL, AWS-LC, BoringSSL, LibreSSL and quictls) - `rustls` - `Schannel` - `wolfSSL` diff --git a/docs/ECH.md b/docs/ECH.md index 2a670edd01ca..8a0153209d8c 100644 --- a/docs/ECH.md +++ b/docs/ECH.md @@ -8,8 +8,8 @@ SPDX-License-Identifier: curl We have added support for ECH to curl. It can use HTTPS RRs published in the DNS if curl uses DoH, or else can accept the relevant ECHConfigList values -from the command line. This works with OpenSSL, wolfSSL, BoringSSL, AWS-LC -or rustls-ffi as the TLS provider. +from the command line. This works with AWS-LC, BoringSSL, OpenSSL, Rustls or +wolfSSL as the TLS provider. This feature is EXPERIMENTAL. DO NOT USE IN PRODUCTION. @@ -135,7 +135,7 @@ LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo There is a reason to want this command line option - for use before publishing 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/). +publishing ECHConfigList values](https://datatracker.ietf.org/doc/html/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 @@ -153,7 +153,7 @@ LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo ``` At that point, you could copy the base64 encoded value above and try again. -For now, this only works for the OpenSSL and BoringSSL/AWS-LC builds. +For now, this only works for the OpenSSL and AWS-LC/BoringSSL builds. ## Default settings @@ -338,11 +338,11 @@ 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 +The AWS-LC/BoringSSL 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. -The BoringSSL/AWS-LC APIs however do not support the `--ech pn:` command +The AWS-LC/BoringSSL APIs however do not support the `--ech pn:` command line variant as of now. ## wolfSSL build @@ -405,7 +405,7 @@ Then there are some functional code changes: 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.) +AWS-LC/BoringSSL. (Both are reasonable.) ## Additional notes @@ -471,7 +471,7 @@ 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 +Both our OpenSSL fork and AWS-LC/BoringSSL have APIs for both controlling GREASE and accessing and logging `retry_configs`, it seems wolfSSL has neither. ### Testing ECH diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md index 43fc0fdeed88..9e39a9b36cb5 100644 --- a/docs/EXPERIMENTAL.md +++ b/docs/EXPERIMENTAL.md @@ -43,6 +43,23 @@ Graduation requirements: - Using HTTP/3 with the given build should perform without risking busy-loops +### HTTP/3 proxy and CONNECT-UDP support + +Support for HTTP/3 proxy and CONNECT-UDP tunneling is experimental and +requires an explicit build-time opt-in (`--enable-proxy-http3` for +autotools, `-DUSE_PROXY_HTTP3=ON` for CMake). + +Graduation requirements: + +- implementation stability over time with no known severe regressions + +### The Quiche backend + +Graduation requirements: + +- the library needs to consider itself non-beta. +- a reasonable expectation of a stable API going forward. + ### The Rustls backend Graduation requirements: diff --git a/docs/FAQ.md b/docs/FAQ.md index 05f7eda38299..6cdf97d32725 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -33,7 +33,7 @@ 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 +libcurl is free, thread-safe, IPv6 compatible, feature-rich, well supported and fast. ### curl @@ -176,7 +176,7 @@ 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 issues can also be taken 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? @@ -204,20 +204,20 @@ 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 +(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 +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 +If you want the most recent collection of CA certs that Mozilla Firefox uses, +we recommend 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? +## 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 @@ -253,7 +253,7 @@ 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 +Many more details are found in the [contribute](https://curl.se/dev/contribute.html) and [internals](https://curl.se/dev/internals.html) documents. @@ -276,7 +276,7 @@ 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 +The reason why static libraries are 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 @@ -294,10 +294,10 @@ 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). +curl can be built to use one of the following SSL alternatives: AWS-LC, +BoringSSL, GnuTLS, LibreSSL, OpenSSL, mbedTLS, Rustls, Schannel (native +Windows), or wolfSSL. 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? @@ -368,7 +368,7 @@ 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. +which implies the *no body* option sent to libcurl. ## How can I disable the Accept: header? @@ -634,7 +634,7 @@ does for you, you can override those request methods by specifying `-X `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 +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]`. @@ -854,11 +854,11 @@ results and fetches the new URL. 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 +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. +SSL. `ftps://` connections default to port 990. -To use explicit FTPS, you use an `FTP://` URL and the `--ssl-reqd` option (or +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. @@ -893,7 +893,7 @@ 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) +## `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: @@ -986,7 +986,7 @@ 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 +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. @@ -1017,7 +1017,7 @@ 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); + mem->memory = realloc(mem->memory, mem->size + realsize + 1); if(mem->memory) { memcpy(&(mem->memory[mem->size]), ptr, realsize); mem->size += realsize; diff --git a/docs/HISTORY.md b/docs/HISTORY.md index c376905ebbae..3723052f9e95 100644 --- a/docs/HISTORY.md +++ b/docs/HISTORY.md @@ -82,8 +82,8 @@ OpenSSL took over and SSLeay was abandoned. May: first Debian package. -August: LDAP:// and FILE:// support added. The curl website gets 1300 visits -weekly. Moved site to curl.haxx.nu. +August: `ldap://` and `file://` support added. The curl website gets 1300 +visits weekly. Moved site to curl.haxx.nu. September: Released curl 6.0. 15000 lines of code. @@ -124,7 +124,7 @@ deemed "GPL incompatible".) March 22: curl supports HTTP 1.1 starting with the release of 7.7. This also introduced libcurl's ability to do persistent connections. 24000 lines of code. The libcurl major SONAME number was bumped to 2 due to this overhaul. -The first experimental ftps:// support was added. +The first experimental `ftps://` support was added. August: The curl website gets 8000 visits weekly. Curl Corporation contacted Daniel to discuss "the name issue". After Daniel's reply, they have never @@ -465,7 +465,7 @@ December 21: dropped hyper ## 2025 -February 5: first 0RTT for QUIC, ssl session import/export +February 5: first 0RTT for QUIC, SSL session import/export February: experimental HTTPS RR support diff --git a/docs/HTTP3.md b/docs/HTTP3.md index 77fa9664278c..74254233b21b 100644 --- a/docs/HTTP3.md +++ b/docs/HTTP3.md @@ -215,14 +215,16 @@ but in case of problems, we recommend their latest release tag. ## Build -Build quiche and BoringSSL: +Build quiche and BoringSSL (described here for quiche v0.29.1, the locations +where BoringSSL is to be found vary with version): - % git clone --depth 1 --branch 0.24.7 --recursive https://github.com/cloudflare/quiche + % git clone --depth 1 --branch 0.29.1 --recursive https://github.com/cloudflare/quiche % cd quiche % cargo build --package quiche --release --features ffi,pkg-config-meta,qlog % ln -s libquiche.so target/release/libquiche.so.0 - % mkdir quiche/deps/boringssl/src/lib - % ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) quiche/deps/boringssl/src/lib/ + % mkdir -p boringssl/lib + % find target/release \( -name libcrypto.a -o -name libssl.a \) -exec ln -vnf -- '{}' boringssl/lib \; + % find target/release/build/boring-sys-*/out/boringssl/src -maxdepth 1 \( -name include \) -exec ln -vsf -- '../{}' boringssl \; Build curl: @@ -230,8 +232,8 @@ Build curl: % git clone --depth 1 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 --with-openssl=$PWD/../quiche/boringssl \ + --with-quiche=$PWD/../quiche/target/release % make % make install diff --git a/docs/HTTPSRR.md b/docs/HTTPSRR.md index bb96526b3985..fbdebc7fde16 100644 --- a/docs/HTTPSRR.md +++ b/docs/HTTPSRR.md @@ -51,7 +51,7 @@ or The list of ALPN IDs is parsed but may not be completely respected because of what the HTTP version preference is set to, which is a problem we are working -on. Also, getting an `HTTP/1.1` ALPN in the HTTPS RR field for an HTTP:// +on. Also, getting an `HTTP/1.1` ALPN in the HTTPS RR field for an `http://` transfer should imply switching to HTTPS, HSTS style. Which curl currently does not. diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md index 2f24845cddc4..a4f8b5843356 100644 --- a/docs/INFRASTRUCTURE.md +++ b/docs/INFRASTRUCTURE.md @@ -139,7 +139,7 @@ anycast access to the site. Should be snappy from virtually everywhere across the globe. The CDN servers support HTTP/1, HTTP/2 and HTTP/3. They set HSTS for a year. -The `HTTP://` version of the site redirects to `HTTPS://`. +The `http://` version of the site redirects to `https://`. Fastly manages the TLS certificates from Let's Encrypt for the servers they run on the behalf of curl. diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index a84faf72f209..64975ab63073 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -103,16 +103,6 @@ arguments in the build. Building statically is not for the faint of heart. -### Fallback for CMake before version 3.13 - -CMake before version 3.13 does not support the `--build` option. In that -case, you have to `cd` to the build directory and use the building tool that -corresponds to the build files that CMake generated for you. This example -assumes that CMake generates `Makefile`: - - $ cd ../curl-build - $ make - # Testing (The test suite does not yet work with the cmake build) @@ -129,16 +119,6 @@ to set a custom install prefix for curl, set [`CMAKE_INSTALL_PREFIX`](https://cmake.org/cmake/help/latest/variable/CMAKE_INSTALL_PREFIX.html) when configuring the CMake build. -### Fallback for CMake before version 3.15 - -CMake before version 3.15 does not support the `--install` option. In that -case, you have to `cd` to the build directory and use the building tool that -corresponds to the build files that CMake generated for you. This example -assumes that CMake generates `Makefile`: - - $ cd ../curl-build - $ make install - # CMake usage This section describes how to locate and use curl/libcurl from CMake-based @@ -262,6 +242,7 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) ## Enabling features - `CURL_ENABLE_NTLM`: Enable NTLM support. Default: `OFF` +- `CURL_ENABLE_SMB`: Enable SMB. 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. @@ -269,9 +250,10 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `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_SSLS_EXPORT`: Enable experimental SSL session import/export. Default: `OFF` +- `USE_ECH`: Enable ECH support (experimental). Default: `OFF` +- `USE_HTTPSRR`: Enable HTTPS RR support (experimental). Default: `OFF` +- `USE_PROXY_HTTP3`: Enable HTTP/3 proxy support (experimental). Default: `OFF` +- `USE_SSLS_EXPORT`: Enable SSL session import/export (experimental). Default: `OFF` - `USE_WIN32_IDN`: Use WinIDN for IDN support. Default: `OFF` - `USE_WIN32_LDAP`: Use Windows LDAP implementation. Default: `ON` @@ -315,7 +297,6 @@ 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_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_SRP`: Disable TLS-SRP support. Default: `OFF` @@ -332,6 +313,17 @@ target_link_libraries(my_target PRIVATE CURL::libcurl) - `CURL_BUILDINFO`: Print `buildinfo.txt` if set. - `CURL_CI`: Assume running under CI if set. +## Environment (via CMake) + +- `CC`: Set C compiler. Alternative to `CMAKE_C_COMPILER` option. +- `CFLAGS`: Pass custom C compiler flags. Alternative to `CMAKE_C_FLAGS` option. +- `CMAKE_GENERATOR`: Alternative to `-G` command-line option. +- `DESTDIR`: Set install destination directory. +- `LDFLAGS`: Pass custom linker flags. + +Details via CMake +[envvars](https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html). + ## CMake options - `CMAKE_BUILD_TYPE`: (see CMake) @@ -367,7 +359,7 @@ Details via CMake - `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_RUSTLS`: Enable Rustls for SSL/TLS. Default: `OFF` +- `CURL_USE_RUSTLS`: Enable Rustls for SSL/TLS (experimental). Default: `OFF` - `CURL_USE_SCHANNEL`: Enable Windows native SSL/TLS (Schannel). Default: `OFF` - `CURL_USE_WOLFSSL`: Enable wolfSSL for SSL/TLS. Default: `OFF` - `CURL_ZLIB`: Use zlib (`ON`, `OFF` or `AUTO`). Default: `AUTO` @@ -376,7 +368,7 @@ Details via CMake - `USE_LIBIDN2`: Use libidn2 for IDN support. Default: `ON` - `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_QUICHE`: Use quiche library for HTTP/3 support (experimental). Default: `OFF` ## Dependency options (via CMake) @@ -501,11 +493,12 @@ Examples: - `APXS`: Absolute path. Default: search for `apxs` - `CADDY`: Absolute path. Default: search for `caddy` +- `H2O`: Absolute path. Default: search for `h2o` - `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` +- `VSFTPD`: Absolute path. Default: search for `vsftpd` - `SSHD`: Absolute path. Default: search for `sshd` - `SFTPD`: Absolute path. Default: search for `sftp-server` diff --git a/docs/INSTALL.md b/docs/INSTALL.md index db743e0554e8..467aa64c058f 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -146,7 +146,7 @@ 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) +- OpenSSL: `--with-openssl` (also for AWS-LC, BoringSSL, LibreSSL, and quictls) - Rustls: `--with-rustls` - Schannel: `--with-schannel` - wolfSSL: `--with-wolfssl` @@ -486,7 +486,7 @@ install `libssl.a` and `libcrypto.a` to `$TOOLCHAIN/sysroot/usr/lib` and copy for Android using OpenSSL like this: ```sh -# For OpenSSL/BoringSSL. In general, you need to the SSL/TLS layer's transitive +# For BoringSSL/OpenSSL. 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" diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md index c145690a2caf..ff59f733e048 100644 --- a/docs/INTERNALS.md +++ b/docs/INTERNALS.md @@ -24,18 +24,22 @@ want it to remain functional and buildable with these and later versions ## Dependencies -We aim to support these or later versions. +We aim to support these or later versions: - 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) +- libgsasl 1.6.0 (2010-12-14) +- libpsl 0.16.0 (2016-12-10) +- LibreSSL 2.9.1 (2019-04-21) - 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) +- nghttp3 1.0.0 (2023-10-15) +- ngtcp2 1.0.0 (2023-10-15), with OpenSSL 3.5.0+: 1.12.0 (2025-04-16) - OpenLDAP 2.0 (2000-08-01) - OpenSSL 3.0.0 (2021-09-07) - Windows Vista 6.0 (2006-11-08 - 2012-04-10) @@ -47,9 +51,9 @@ We aim to support these or later 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: +these or later versions: -- clang-tidy 17.0.0 (2023-09-19), recommended: 19.1.0 or later (2024-09-17) +- clang-tidy 17.0.0 (2023-09-19), recommended: 19.1.0 (2024-09-17) - cmake 3.18 (2020-07-15) - GNU autoconf 2.59 (2003-11-06) - GNU automake 1.7 (2002-09-25) diff --git a/docs/KNOWN_BUGS.md b/docs/KNOWN_BUGS.md index a3b0d37889a8..359c2fab9955 100644 --- a/docs/KNOWN_BUGS.md +++ b/docs/KNOWN_BUGS.md @@ -25,14 +25,6 @@ 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) @@ -45,10 +37,6 @@ 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) @@ -60,6 +48,10 @@ 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) +## ECH not working through Proxy Tunnels + +[curl issue 22043](https://github.com/curl/curl/issues/22043) + # Email protocols ## IMAP `SEARCH ALL` truncated response @@ -219,10 +211,20 @@ https://curl.se/mail/lib-2012-07/0073.html # Authentication +## `--aws-sigv4` does not handle multipart/form-data correctly + +[curl issue 13351](https://github.com/curl/curl/issues/13351) + ## Digest `auth-int` for PUT/POST We do not support auth-int for Digest using PUT or POST +## Digest does not care for `domain` + +libcurl ignores the `domain` directive in Digest authentication challenges +(`WWW-Authenticate:`). RFC 7616 defines it as a quoted, space-separated list +of URIs that define the protection space. + ## MIT Kerberos for Windows build libcurl fails to build with MIT Kerberos for Windows (`KfW`) due to its @@ -422,7 +424,7 @@ 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. +it requires a separate option. See [curl issue 14481](https://github.com/curl/curl/issues/14481) @@ -490,42 +492,6 @@ 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 diff --git a/docs/MANUAL.md b/docs/MANUAL.md index e6f5123d2b5b..fb62f64ba24c 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -99,8 +99,8 @@ or specify them with the `-u` flag like 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 +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 the `--ssl-reqd` option. ### SFTP / SCP @@ -188,13 +188,13 @@ transfers, and curl's `-v` option to see exactly what curl is sending. ## Piping -Get a key file and add it with `apt-key` (when on a system that uses `apt` for -package management): +Get a key file and install it as a trusted one (when on a system that uses +`apt` for package management): - curl -L https://apt.example.org/llvm-snapshot.gpg.key | sudo apt-key add - + curl -L https://apt.example.org/llvm-snapshot.gpg.key | sudo tee + /etc/apt/trusted.gpg.d/llvm-snapshot.asc >/dev/null -The '|' pipes the output to STDIN. `-` tells `apt-key` that the key file -should be read from STDIN. +The '|' pipes the output to stdin. `tee` reads from stdin. ## Ranges diff --git a/docs/Makefile.am b/docs/Makefile.am index 77971ac77176..fdc4511b5cab 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -53,6 +53,7 @@ INTERNALDOCS = \ internals/CLIENT-WRITERS.md \ internals/CODE_STYLE.md \ internals/CONNECTION-FILTERS.md \ + internals/CREDENTIALS.md \ internals/CURLX.md \ internals/DYNBUF.md \ internals/HASH.md \ @@ -61,6 +62,7 @@ INTERNALDOCS = \ internals/MQTT.md \ internals/MULTI-EV.md \ internals/NEW-PROTOCOL.md \ + internals/PEERS.md \ internals/PORTING.md \ internals/RATELIMITS.md \ internals/README.md \ diff --git a/docs/RELEASE-PROCEDURE.md b/docs/RELEASE-PROCEDURE.md index 36c786cbfe90..e62a90cbd743 100644 --- a/docs/RELEASE-PROCEDURE.md +++ b/docs/RELEASE-PROCEDURE.md @@ -97,7 +97,7 @@ pending release: - Release candidate two (**rc2**) ships nine days later, sixteen days before the release. On a Monday. Tagged like `rc-7_34_0-2`. -- Release candidate tree (**rc3**) ships nine days later, seven days before +- Release candidate three (**rc3**) ships nine days later, seven days before the release. On a Wednesday. Tagged like `rc-7_34_0-3`. Release candidate tarballs are ephemeral and each such tarball is only kept @@ -125,11 +125,9 @@ push for it. Based on the description above, here are some planned future release dates: -- March 11, 2026 -- April 29, 2026 - June 24, 2026 -- August 19, 2026 -- October 14, 2026 -- December 9, 2026 -- February 3, 2027 -- March 31, 2027 +- September 2, 2026 +- October 28, 2026 +- December 23, 2026 +- February 17, 2027 +- April 14, 2027 diff --git a/docs/RUSTLS.md b/docs/RUSTLS.md index b1c8044e8e3e..ed032f7fb628 100644 --- a/docs/RUSTLS.md +++ b/docs/RUSTLS.md @@ -56,7 +56,7 @@ Once downloaded, build `curl` using `--with-rustls` and the path to the extracte Building `rustls-ffi` from source requires both a rust compiler, and the [cargo-c] cargo plugin. To install a Rust compiler, use [rustup] or your package manager to install -the **1.73+** or newer toolchain. +the **1.73** or newer toolchain. To install `cargo-c`, use your [package manager][cargo-c pkg], download [a pre-built archive][cargo-c prebuilt], or build it from source with `cargo install cargo-c`. diff --git a/docs/SECURITY-ADVISORY.md b/docs/SECURITY-ADVISORY.md index 4f3e1df2c9ad..34c342509d57 100644 --- a/docs/SECURITY-ADVISORY.md +++ b/docs/SECURITY-ADVISORY.md @@ -6,11 +6,14 @@ SPDX-License-Identifier: curl # Anatomy of a curl security advisory -As described in the [Security Process](https://curl.se/dev/secprocess.html) -document, when a security vulnerability has been reported to the project and -confirmed, we author an advisory document for the issue. It should ideally -be written in cooperation with the reporter to make sure all the angles and -details of the problem are gathered and described correctly and succinctly. +As described in the [vulnerability disclosure +policy](https://curl.se/dev/vuln-disclosure.html), when a vulnerability has +been reported to the project and and it has been confirmed by the team, we +author an advisory document for the issue. + +

This advisory document should ideally be written in cooperation with the +reporter to make sure all the angles and details of the problem are gathered +and described correctly and succinctly. ## New document @@ -31,13 +34,22 @@ in the same directory. It holds a large array with all published curl vulnerabilities. All fields should be filled in accordingly, separated by a 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`) +The fields for every CVE in `vuln.pm` are, in order: + +1. HTML page name +2. first vulnerable version +3. last vulnerable version +4. name of the issue +5. CVE Id +6. announce date (`YYYYMMDD`) +7. report to the project date (`YYYYMMDD`) +8. CWE +9. awarded reward amount (USD) +10. area (single word) +11. C-issue (`-` if not a C issue at all, `OVERFLOW` , `OVERREAD`, `DOUBLE_FREE`, `USE_AFTER_FREE`, `NULL_MISTAKE`, `UNINIT`, `BAD_FREE`) +12. affected components: `both`, `lib` or `tool` +13. severity: `low`, `medium`, `high` or `critical` +14. URL to the initial report (often on HackerOne) ### `Makefile` diff --git a/docs/SSLCERTS.md b/docs/SSLCERTS.md index 3506fbd787d1..d5110e18f0f4 100644 --- a/docs/SSLCERTS.md +++ b/docs/SSLCERTS.md @@ -71,6 +71,10 @@ cert file named `curl-ca-bundle.crt` in these directories and in this order: curl 8.11.0 added a build-time option to disable this search behavior, and another option to restrict search to the application's directory. +curl 8.19.0 added a build-time option to enable Native CA by default on +Windows. This build-time option by default also disables searching for +a `curl-ca-bundle.crt` on disk. + ### Use the native store In several environments, in particular on Microsoft and Apple operating diff --git a/docs/THANKS b/docs/THANKS index e02c36170096..a70bf7527dd1 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -6,6 +6,8 @@ 0xee on github 0xflotus on github +0xN3R3K3 +11soda11 12932 on github 1337vt on github 1ocalhost on github @@ -50,6 +52,7 @@ Adrian Burcea Adriano Meirelles Adrian Peniak Adrian Schuur +Ady Elouej afengsoft on github afrind on github Aftab Alam @@ -58,6 +61,7 @@ ahodesuka on github aisle-research-bot ajak in #curl Ajit Dhumale +A Johnston Akhilesh Nema Akhil Kedia Aki Koskinen @@ -67,6 +71,7 @@ Akshay Vernekar Alain Danteny Alain Miniussi Alan Coopersmith +Alan De Smet Alan Jenkins Alan Pinstein Albert Chin-A-Young @@ -151,7 +156,9 @@ Alex Vinnik Alex Xu Alfonso Martone Alfred Gebert +alhudz Alice Lee Poetics +alienowo on hackerone Ali Khodkar ALittleDruid on github Ali Utku Selen @@ -159,8 +166,10 @@ Allen Pulsifer Alois Klink Alona Rossen Amaury Denoyelle +ambikeesshh Ameda Amahru amishmm on github +amitbidlan Amit Katyal Ammar Faizi Amol Pattekar @@ -229,6 +238,7 @@ Andrew Kurushin Andrew Kvalheim Andrew Lambert Andrew Moise +Andrew Nesbitt Andrew Olsen Andrew Potter Andrew Robbins @@ -287,6 +297,7 @@ Aquila Macedo arainchik on github Archangel_SDY on github Arian van Putten +Aritra Basu Arjan van de Ven Arkadiusz Miskiewicz Arkadi Vainbrand @@ -330,6 +341,7 @@ Axel Tillequin Ayesh Karunaratne Ayoub Boudhar Ayushman Singh Chauhan +azraelxuemo on hackerone b9a1 on github Bachue Zhou Baitinq on github @@ -344,15 +356,18 @@ BANADDA baranyaib90 on github Barry Abrahamson Barry Pollard +Bartel Sielski Bartosz Ruszczak Bart Whiteley Baruch Siach Bas Mevissen +Bastian Jesuiter Bastian Krause Bastien Bouclet Basuke Suzuki Bas van Schaik baumanj on github +BazaarAcc32 on github bdry on github beckenc on github behindtheblackwall on hackerone @@ -402,6 +417,7 @@ Bill Egert Bill Hoffman billionai on github Bill Middlecamp +Bill Mill Bill Nagel Bill Pyne Billy O'Neal @@ -501,6 +517,7 @@ buzo-ffm on github bxac on github Bylon2 on github Byrial Jensen +ByteRay on hackerone Cajus Pollmeier Caleb Raitto calm329 @@ -602,12 +619,14 @@ Christopher Reid Christopher R. Palmer Christopher Sauer Christopher Stone +Christopher Wellons Christoph Jabs Christoph Krey Christoph M. Becker Christoph Reiter Chris Webb Chris Young +chrizilla on github chrysos349 on github Chungtsun Li Ciprian Badescu @@ -625,6 +644,7 @@ Clint Clayton Cloudogu Siebels CMD cmfrolick on github +co-authors in libssh2 codesniffer13 on github Cody Jones Cody Mack @@ -751,6 +771,7 @@ Dan Zitter Daphne Luong Darío Hereñú Dario Nieuwenhuis +Dario Vinella Dario Weißer Darren Banfi Darryl House @@ -766,6 +787,7 @@ Dave Nicolson Dave Reisner Dave Thompson Dave Vasilevsky +Dave Walker Davey Shafik David Bau David Benjamin @@ -818,6 +840,7 @@ David Woodhouse David Wright David Yan David Zhuang +daviey on hackerone Da-Yoon Chung dbalsom dbrowndan on github @@ -943,6 +966,7 @@ Duy Phan Thanh Dwarakanath Yadavalli dwickr Dwij Mehta +dyingc on github Dylam De La Torre Dylan Anthony Dylan Ellicott @@ -980,6 +1004,7 @@ elelel on github elephoenix on github Elia Tufarolo Eli Schwartz +Elise Vance Elliot Killick Elliot Saba Elliott Balsley @@ -993,6 +1018,7 @@ emanruse on github Emanuele Bovisio Emanuele Torre Emanuel Komínek +Emanuel Krollmann Emil Engler Emiliano Ida Emilio Cobos Álvarez @@ -1059,9 +1085,11 @@ Ethan Glasser Camp Ethan Wilkes Etienne Simard Eugene Kotlyarov +Eunsoo Kim Evangelos Foutras Evan Jordan Even Rouault +evergarden1123 on hackerone Evert Pot Evgeny Grin (Karlson2k) Evgeny Turnaev @@ -1105,6 +1133,7 @@ Felix Yan Feng Tu Fernando Muñoz ffath-vo on github +Filipe Casal Filip Lundgren Filip Salomonsson finkjsc on github @@ -1170,6 +1199,7 @@ galen11 on github Gambit Communications Ganesh Kamath Ganesh Viswanathan +Gao Liyou gaoxingwang on github Garrett Holmstrom Garrett Squire @@ -1255,6 +1285,8 @@ Griffin Downs Grigory Entin Grisha Levit Gruber Glass +Guancheng Li +Guannan Wang gudyuu on hackerone Guenole Bescon Guido Berhoerster @@ -1313,6 +1345,7 @@ Helge Klein Helmut Grohne Helmut K. C. Tessarek Helwing Lutz +Hem Parekh Hendrik Visage Henning Schild Henri Gomez @@ -1347,6 +1380,7 @@ Howard Blaise Howard Chu hsiao yi HsiehYuho on github +htasta htasta on github huanghuihui0904 Hubert Kario @@ -1528,6 +1562,7 @@ Jean-Philippe Barrette-LaPierre Jean-Philippe Menil Jeff Connelly Jeff Hodges +jeffhuang Jeff Johnson Jeff King Jeff Lawson @@ -1553,6 +1588,7 @@ Jeremy Huddleston Jeremy Lainé Jeremy Lin Jeremy Maitin-Shepard +Jeremy Nicoll Jeremy Pearson Jérémy Rabasco Jérémy Rocher @@ -1580,6 +1616,7 @@ jhauga jhoyla on github Jiacai Liu Jiang Wenjian +Jiashuo Liang Jiawen Geng Jicea Jie He @@ -1603,6 +1640,7 @@ Jishan Shaikh Jiwoo Park Jixinqi Jiyong Yang +jjchuck on hackerone jkamp-aws on github jmaggard10 on github jmdavitt on github @@ -1631,6 +1669,7 @@ Johannes Ernst Johannes G. Kristinsson Johannes Lesr Johannes Schindelin +Johannes Schlatow Johan Nilsson Johann Sebastian Schicho Johan van Selst @@ -1714,6 +1753,7 @@ Jordan Brown Jörg Mueller-Tolk Jörn Hartroth Jose Alf +Josef Cejka Josef Wolf José Joaquín Atria Jose Kahan @@ -2102,6 +2142,7 @@ Mark Brand Mark Butler Mark Davies Mark Dodgson +Mark Esler Mark Gaiser Mark Hamilton Mark Huang @@ -2310,6 +2351,7 @@ Miguel Angel Miguel Diaz migueljcrum on github Mihai Ionescu +mik Mikael Johansson Mikael Sennerholm Mikalai Ananenka @@ -2324,6 +2366,7 @@ Mike Giancola Mike Hasselberg Mike Henshaw Mike Hommey +Mike-menny on github Mike Mio Mike Norton Mike Power @@ -2372,6 +2415,7 @@ Muhamad Arga Reksapati Muhammad Herdiansyah Muhammad Hussein Ammari Muhammed Yavuz Nuzumlalı +mulan_dh on hackerone Murugan Balraj musvaage on github Muz Dima @@ -2412,6 +2456,7 @@ Neil Spring NeimadTL nekopsykose on github Nemos2024 on github +netspacer.research neutric on github nevakrien on github nevv on HackerOne/curl @@ -2523,6 +2568,7 @@ opensignature on github opensslonzos-github on github Ophir Lojkine Orange Tsai +oreadvanthink on github Oren Souroujon Oren Tirosh Orgad Shaneh @@ -2547,6 +2593,7 @@ Palo Markovic pandada8 on github Paolo Mossino Paolo Piacentini +parasol-aser Paras Sethia parazyd on github Pascal Gaudette @@ -2621,6 +2668,7 @@ pendrek at hackerone Peng Li Peng-Yu Chen pennae on github +penpal Per Jensen Per Lundberg Per Malmberg @@ -2670,6 +2718,7 @@ Phil E. Taylor Philip Chan Philip Craig Philip Gladstone +Philip H. Philip Heiduck Philip Langdale Philippe Antoine @@ -2778,6 +2827,7 @@ Rasmus Thomsen Raul Onitza-Klugman Ravi Pratap Ray Dassen +Raymond Steen Ray Pekowski Ray Satiro Razvan Cojocaru @@ -2801,6 +2851,7 @@ Renaud Guillard Renaud Lehoux Rene Bernhardt Rene Rebe +renjian on hackerone renovate[bot] renovate[bot] RepoRascal on hackerone @@ -3005,6 +3056,7 @@ Sascha Swiercy Sascha Zengler Satadru Pramanik Satana de Sant'Ana +Saud Alshareef Saul good saurabhsingh-dev on github Saurav Babu @@ -3061,6 +3113,7 @@ Sergii Pylypenko Sergio Ballestrero Sergio Barresi Sergio Borghese +Sergio Correia Sergio Durigan Junior Sergio-IME on github Sergio Mijatovic @@ -3089,6 +3142,7 @@ Sh Diao Sheshadri.V Shikha Sharma Shine Fan +Shintomon Mathew Shiraz Kanga shithappens2016 on github Shlomi Fish @@ -3096,6 +3150,7 @@ Shmulik Regev Shohei Maeda Siddhartha Prakash Jain siddharthchhabrap on github +sideshowbarker on github Sidney San Martín Siegfried Gyuricsko silveja1 on github @@ -3120,10 +3175,13 @@ smuellerDD on github sn on hackerone sofaboss on github Sohom Datta +Sollace on github Somnath Kundu Song Ma +Song X. Gao Sonia Subramanian Sören Tempel +sourceturner southernedge on github Spacen Jasset spectreglobalsec on hackerone @@ -3308,6 +3366,7 @@ Tim Friedrich Brüggemann Tim Harder Tim Heckman Tim Hill +Tim Martin Tim Mcdonough Timmy Schierling Tim Newsome @@ -3338,6 +3397,7 @@ tlahn on github tmkk on github Tobias Blomberg Tobias Bora +Tobias Frauenschläger Tobias Gabriel Tobias Hieta Tobias Hintze @@ -3452,9 +3512,12 @@ Valerii Zapodovnikov vanillajonathan on github Varnavas Papaioannou Vasiliy Faronov +Vasiliy-Kkk Vasiliy Ulyanov Vasily Lobaskin Vasy Okhin +vectorqueue on hackerone +vegagent on hackerone Venkat Akella Venkataramana Mokkapati Venkat Krishna R @@ -3479,6 +3542,7 @@ Vincent Le Normand Vincent Penquerc'h Vincent Sanders Vincent Torri +violet12331 on hackerone violetlige on github vitaha85 on github Vitaly Varyvdin @@ -3532,6 +3596,7 @@ Wez Furlong Wham Bang Wilfredo Sanchez Wilhelm von Thiele +Will Cosgrove Will Dietz Willem Hoek Willem Sparreboom @@ -3548,6 +3613,7 @@ Wojciech Zwiefka wolfsage on hackerone Wolf Vollprecht Wouter Van Rooy +wulin-nudt on github Wu Yongzheng Wu Zheng wxiaoguang on github @@ -3624,6 +3690,7 @@ Yves Lejeune YX Hao z2_ Zachary Seguin +Zartaj Majeed Zdenek Pavlas Zekun Ni zelinchen on github @@ -3635,6 +3702,8 @@ Zespre Schmidt zhanghu on xiaomi Zhang Wen Zhang Xiuhua +zhanhb on github +Zhanpeng Liu Zhaoming Luo Zhaoyang Wu Zhao Yisha diff --git a/docs/THANKS-filter b/docs/THANKS-filter index cc964a49b2a9..f9e7e1c8d2d4 100644 --- a/docs/THANKS-filter +++ b/docs/THANKS-filter @@ -25,7 +25,7 @@ # This is a list of names we have recorded that already are thanked # appropriately in THANKS. This list contains variations of their names and # their "canonical" name. This file is used for scripting purposes to avoid -# duplicate entries and will not be included in release tarballs. +# duplicate entries and is not included in release tarballs. # When removing dupes that are not identical names from THANKS, add a line # here! # @@ -162,3 +162,4 @@ s/Maksim Sciepanienka/Maksim Ściepanienka/ s/Qriist.*/Qriist on github/ s/Viktor Szakatas/Viktor Szakats/ s/Val S\./Valerie Snyder/ +s/Andrew Nesbit$/Andrew Nesbitt/ diff --git a/docs/TODO.md b/docs/TODO.md index 2be796f8c92f..15d6c02ba0e2 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -687,7 +687,7 @@ 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://` +## 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 @@ -791,7 +791,7 @@ done, and thus maintain its connection pool, DNS cache and more. 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/). +limits](https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-ratelimit-headers/). See [curl issue 5406](https://github.com/curl/curl/issues/5406) diff --git a/docs/TheArtOfHttpScripting.md b/docs/TheArtOfHttpScripting.md index 7f300f070395..541cb4c2ff71 100644 --- a/docs/TheArtOfHttpScripting.md +++ b/docs/TheArtOfHttpScripting.md @@ -591,7 +591,7 @@ 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 +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 diff --git a/docs/URL-SYNTAX.md b/docs/URL-SYNTAX.md index 45b6f5ab454c..219e84ee932c 100644 --- a/docs/URL-SYNTAX.md +++ b/docs/URL-SYNTAX.md @@ -11,8 +11,8 @@ 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) +- [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 @@ -151,10 +151,9 @@ schemes: ## Userinfo -The userinfo field can be used to set username and password for -authentication purposes in this transfer. The use of this field is discouraged -since it often means passing around the password in plain text and is thus a -security risk. +The userinfo field can be used to set username and password for authentication +purposes in this transfer. The use of this field is discouraged since it often +means passing around the password in plain text and is thus a security risk. URLs for IMAP, POP3 and SMTP also support *login options* as part of the userinfo field. They are provided as a semicolon after the password and then @@ -176,6 +175,40 @@ brackets). For example: https://[2001:1890:1112:1::20]/ +libcurl rejects hostnames with more than one trailing dot. + +### Numerical IPv4 addresses + +libcurl parses and normalizes everything that appears to be a numerical IPv4 +address. Including octal and hexadecimal formats and using one, two, three or +four number groups. + +This normalizing is done so that curl can properly get documents from HTTP +servers (with the correctly formatted address in the `Host:` header), so that +IP based filtering for things like the `NO_PROXY` environment variable has a +higher chance of working correctly, to increase the chances that two URLs can +be compared and to allow users to extract and visualize the address in a readable +way and to make sure libcurl works identically across different name resolver +libraries and function calls. + +For a hostname that is only an IPv4 address with a trailing dot, the trailing +dot is removed in the normalizing process. + +### Numerical IPv6 addresses + +libcurl allows a zone id to be provided with a numerical IPv6 address, +separated with a percent character (`%`). The percent character may also be +percent-encoded as `%25`. Like this: + + http://[fe80::1%25eth0]/ + + http://[fe80::1%eth0]/ + +### `IPvFuture` + +RFC 3986 documents a numerical IP address format called `IPvFuture`. libcurl +does not recognize this format. Using it causes parse errors. + ### "localhost" Starting in curl 7.77.0, curl uses loopback IP addresses for the name @@ -212,6 +245,14 @@ 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 +## Path + +By default, libcurl removes sequences of `/./` and `/../` from the path as per +RFC 3986. + +libcurl might also normalize percent-encoded sequences to use uppercase +hexadecimal letters. + # Scheme specific behaviors ## FTP @@ -228,7 +269,7 @@ value of the ASCII code for the slash). ## FILE -When a `FILE://` URL is accessed on Windows systems, it can be crafted in a +When a `file://` URL is accessed on Windows systems, it can be crafted in a way so that Windows attempts to connect to a (remote) machine when curl wants to read or write such a path. diff --git a/docs/VERIFY.md b/docs/VERIFY.md index de2c8b112e24..c18b65a6612c 100644 --- a/docs/VERIFY.md +++ b/docs/VERIFY.md @@ -36,10 +36,24 @@ 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. +For full verification, invoke the script inside an up-to-date curl source code +git repository. Without a git repository present, it does a lighter check by +rebuilding the release using the files in the tarball. + +Note: full verification mode checks out the release tag in your repository. +Run it in a clean working tree (no local changes) or a dedicated clone. + Invoke it like this: + git clone https://github.com/curl/curl + cd curl + mv [download-dir]/curl-8.19.0.tar.xz . ./scripts/verify-release curl-8.19.0.tar.xz +A successful check ends up with a final output similar to: + + curl-8.19.0.tar.xz: OK + 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. @@ -65,15 +79,21 @@ gain trust is to verify and review our testing procedures. - 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 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) +- every single file in the git repository has a clear copyright and license + statement. Complete knowledge and tracking of provenience. + - we actively avoid base64 encoded chunks as they too could function as ways to obfuscate malicious contents +- we forbid and prevent git force push on the master branch. History cannot be + rewritten. + - 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) @@ -92,6 +112,10 @@ gain trust is to verify and review our testing procedures. every commit and every PR. We do not merge commits that have unexplained test failures. +- 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 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. @@ -100,9 +124,9 @@ gain trust is to verify and review our testing procedures. 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 keep running static code analyzers on the code, both traditional ones + (clang-tidy, CodeSonar, Coverity) but also new generation AI powered ones + like Zeropath and Codex Security. - 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 @@ -114,6 +138,14 @@ gain trust is to verify and review our testing procedures. - 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 do reproducible releases to allow anyone to verify that the contents is + untainted + +- we digitally sign releases, git tags and git commits + +- there is a git backup on [codeberg](https://codeberg.org/curl/) for enhanced + resilience to infrastructure disturbance + - we are committed to always fix reported vulnerabilities in the following release. Security problems never linger around once they have been reported. @@ -121,11 +153,16 @@ gain trust is to verify and review our testing procedures. - we document everything and every detail about all curl vulnerabilities ever reported +- our code has been audited several times by external security experts, and + the few issues that have been detected in those were immediately addressed + +- Strong two-factor authentication on GitHub is mandatory for all committers + - 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 +- we have a vulnerability disclosure program that allows researchers to submit + suspected vulnerabilities in a private and secure fashion, so that we can + work on fixing curl and announcing the flaw in a responsible manner to + minimize risks for users. diff --git a/docs/VERSIONS.md b/docs/VERSIONS.md index 0aebbff49731..5db6a51a7673 100644 --- a/docs/VERSIONS.md +++ b/docs/VERSIONS.md @@ -68,13 +68,14 @@ 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.21.0: pending -- 8.20.0: April 29, 2026 -- 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.22.0: pending +- 8.21.0: June 24 2026 +- 8.20.0: April 29 2026 +- 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 - 8.14.0: May 28 2025 - 8.13.0: April 2 2025 diff --git a/docs/VULN-DISCLOSURE-POLICY.md b/docs/VULN-DISCLOSURE-POLICY.md index abc7ef2c0a96..a115ee171622 100644 --- a/docs/VULN-DISCLOSURE-POLICY.md +++ b/docs/VULN-DISCLOSURE-POLICY.md @@ -36,6 +36,17 @@ announcement. [HackerOne](https://hackerone.com/curl). Issues filed there reach a handful of selected and trusted people. +- When communicating in the curl project, please explain your issues or + improvements briefly and clearly in your own human voice. Do not lazily + paste massive, AI-generated explanations; as a contributor doing this + infrequently, it is your responsibility to invest a few extra minutes into + making your message digestible. The maintainers review submissions + constantly, and clear writing reduces their daily burden and friction. + +- The curl project cannot handle vulnerability reports sent to us over email. + We lose track of the reports. We cannot easily disclose them. Please do not + send us reports over email. + - Messages that do not relate to the reporting or managing of an undisclosed security vulnerability in curl or libcurl are ignored and no further action is required. @@ -146,7 +157,7 @@ made public. # Severity levels The curl project's security team rates security problems using four severity -levels depending how serious we consider the problem to be. We use **Low**, +levels depending on how serious we consider the problem to be. We use **Low**, **Medium**, **High** and **Critical**. We refrain from using numerical scoring of vulnerabilities. @@ -218,9 +229,11 @@ 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. -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. +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 bypasses +the regular counter-measures and it causes a never-ending transfer, it might +be a security problem. ## Not practically possible @@ -248,12 +261,20 @@ already do much worse harm and the problem is not really in curl. ## Debug & Experiments Vulnerabilities in features which are off by default (in the build) and -documented as experimental, or exist only in debug mode, are not eligible for a -reward and we do not consider them security problems. +documented as experimental, or exist only in debug mode, are not considered +security problems. The same applies to scripts and software which are not installed by default through the make install rule. +## Test code + +curl has an extensive test suite with lots of code written specifically to +exercise and verify curl, libcurl and specific internal functions. The test +code and its associated test servers are *not* intended for production use. +They are not secure, you should not assume otherwise and must not report about +security problems in those. + ## URL inconsistencies URL parser inconsistencies between browsers and curl are expected and are not @@ -363,6 +384,17 @@ 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. +## Non-released code + +Only curl releases are ever considered *secure*. Between releases, we are +under development and then we may have code present in the git repository that +is insecure, but without those flaws being considered as vulnerabilities. +Another reason we strongly suggest you only use curl release versions in +production. + +Unreleased code may also contain fixes to problems that were present in the +most recent release. + # curl major incident response Vulnerability disclosure manages the full life cycle of a vulnerability @@ -413,7 +445,8 @@ roles: * **incident lead** - Coordinates technical efforts * **communication lead** - Single point of public contact -It is likely that our [BDFL](https://en.wikipedia.org/wiki/Benevolent_dictator_for_life) occupies +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 @@ -422,8 +455,8 @@ contain a brief notification that a **major incident** is occurring. ## Major incident ongoing -During the incident - all press, media, legal or commercial entities should contact -communication leader (security@curl.se). +During the incident - all press, media, legal or commercial entities should +contact communication lead (security@curl.se). Existing **curl-security** team internal communication channels are used for all internal communication. @@ -431,9 +464,9 @@ for all internal communication. Existing vulnerability disclosure process are followed for any embargoes and fixes. -Where possible, public communication are provided: -* regular communication from communication leader (for example daily update) -* asynchronous communication from incident leader +Where possible, public communications are provided: +* regular communication from communication lead (for example daily update) +* asynchronous communication from incident lead * Delivered to the aforementioned curl communication channels. diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index f7236af1b127..f8fd01ccd63e 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -212,6 +212,7 @@ DPAGES = \ proxy-digest.md \ proxy-header.md \ proxy-http2.md \ + proxy-http3.md \ proxy-insecure.md \ proxy-key-type.md \ proxy-key.md \ diff --git a/docs/cmdline-opts/_ENVIRONMENT.md b/docs/cmdline-opts/_ENVIRONMENT.md index 1ac85fb12841..a3c13ae0c00d 100644 --- a/docs/cmdline-opts/_ENVIRONMENT.md +++ b/docs/cmdline-opts/_ENVIRONMENT.md @@ -3,7 +3,8 @@ # ENVIRONMENT The environment variables can be specified in lower case or upper case. The lower case version has precedence. `http_proxy` is an exception as it is only -available in lower case. +available in lower case. (Note that some systems, like Windows, do not +differentiate between environment variables using different case.) Using an environment variable to set the proxy has the same effect as using the --proxy option. diff --git a/docs/cmdline-opts/_GLOBBING.md b/docs/cmdline-opts/_GLOBBING.md index 37c8d430693d..b801adb4d1f9 100644 --- a/docs/cmdline-opts/_GLOBBING.md +++ b/docs/cmdline-opts/_GLOBBING.md @@ -39,4 +39,17 @@ probably have to put the full URL within double quotes to avoid the shell from interfering with it. This also goes for other characters treated special, like for example '&', '?' and '*'. +The separate globbing components can be referenced in the --output option to +allow pieces to be reused in the target filename. + +Starting in curl 8.21.0, the separate globbing parts can be named and +referenced by their names. The case sensitive alphanumeric name is set +enclosed within angle brackets after the opening character. Examples: + + https://fun.example/{one,two,three}.jpg + + ftp://ftp.example.com/file[1-100].txt + +Setting the same glob name twice is an error. + Switch off globbing with --globoff. diff --git a/docs/cmdline-opts/_PROTOCOLS.md b/docs/cmdline-opts/_PROTOCOLS.md index 831b944d24b7..6b1918a9b227 100644 --- a/docs/cmdline-opts/_PROTOCOLS.md +++ b/docs/cmdline-opts/_PROTOCOLS.md @@ -6,7 +6,7 @@ particular build may not support them all. ## DICT Lets you lookup words using online dictionaries. ## FILE -Read or write local files. curl does not support accessing file:// URL +Read or write local files. curl does not support accessing `file://` URL remotely, but when running on Microsoft Windows using the native UNC approach works. Only absolute paths. ## FTP(S) diff --git a/docs/cmdline-opts/_PROXYPREFIX.md b/docs/cmdline-opts/_PROXYPREFIX.md index 297b56c4b64c..0c106306d878 100644 --- a/docs/cmdline-opts/_PROXYPREFIX.md +++ b/docs/cmdline-opts/_PROXYPREFIX.md @@ -1,22 +1,22 @@ # PROXY PROTOCOL PREFIXES -The proxy string may be specified with a protocol:// prefix to specify +The proxy string may be specified with a `protocol://` prefix to specify alternative proxy protocols. (Added in 7.21.7) If no protocol is specified in the proxy string or if the string does not match a supported one, the proxy is treated as an HTTP proxy. The supported proxy protocol prefixes are as follows: -## http:// +## `http://` Makes it use it as an HTTP proxy. The default if no scheme prefix is used. -## https:// +## `https://` Makes it treated as an **HTTPS** proxy. -## socks4:// +## `socks4://` Makes it the equivalent of --socks4 -## socks4a:// +## `socks4a://` Makes it the equivalent of --socks4a -## socks5:// +## `socks5://` Makes it the equivalent of --socks5 -## socks5h:// +## `socks5h://` Makes it the equivalent of --socks5-hostname diff --git a/docs/cmdline-opts/_URL.md b/docs/cmdline-opts/_URL.md index 288b9d0aa713..700e711359d9 100644 --- a/docs/cmdline-opts/_URL.md +++ b/docs/cmdline-opts/_URL.md @@ -4,7 +4,7 @@ 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 +If you provide a URL without a leading `protocol://` scheme, curl guesses what protocol you want. It then defaults to HTTP but assumes others based on often-used hostname prefixes. For example, for hostnames starting with `ftp.` curl assumes you want FTP. @@ -20,9 +20,5 @@ handshakes. This improves speed. Connection reuse can only be done for URLs specified for a single command line invocation and cannot be performed between separate curl runs. -Provide an IPv6 zone id in the URL with an escaped percentage sign. Like in - - 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/ca-native.md b/docs/cmdline-opts/ca-native.md index 4a887df558a6..67fdf8c3acef 100644 --- a/docs/cmdline-opts/ca-native.md +++ b/docs/cmdline-opts/ca-native.md @@ -24,7 +24,7 @@ Use the operating system's native CA store for certificate verification. 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 +This option works with OpenSSL and its forks (BoringSSL, LibreSSL, etc) on Windows (Added in 7.71.0) and on Apple OS when libcurl is built with Apple SecTrust enabled. (Added in 8.17.0) diff --git a/docs/cmdline-opts/follow.md b/docs/cmdline-opts/follow.md index 096324f1654b..b34e30c693d0 100644 --- a/docs/cmdline-opts/follow.md +++ b/docs/cmdline-opts/follow.md @@ -24,7 +24,7 @@ set with --request when following redirects as the HTTP specification says. The method string set with --request is used in subsequent requests for the 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 +This is subtly different than --location, as that option always sets the custom method in all subsequent requests independent of response code. Restrict which protocols a redirect is accepted to follow with --proto-redir. diff --git a/docs/cmdline-opts/ftp-ssl-control.md b/docs/cmdline-opts/ftp-ssl-control.md index a68359a7b635..fa85de24f187 100644 --- a/docs/cmdline-opts/ftp-ssl-control.md +++ b/docs/cmdline-opts/ftp-ssl-control.md @@ -18,3 +18,5 @@ Example: Require SSL/TLS for the FTP login, clear for transfer. Allows secure authentication, but non-encrypted data transfers for efficiency. Fails the transfer if the server does not support SSL/TLS. + +If set, this option overrides --ssl. diff --git a/docs/cmdline-opts/hostpubsha256.md b/docs/cmdline-opts/hostpubsha256.md index e695a10cb588..a92dbe5d7cb9 100644 --- a/docs/cmdline-opts/hostpubsha256.md +++ b/docs/cmdline-opts/hostpubsha256.md @@ -18,6 +18,3 @@ Example: Pass a string containing a Base64-encoded SHA256 hash of the remote host's public key. curl refuses the connection with the host unless the hashes match. - -This feature requires libcurl to be built with libssh2 and does not work with -other SSH backends. diff --git a/docs/cmdline-opts/hsts.md b/docs/cmdline-opts/hsts.md index 0f6673cc4ef1..7653c65cd5d9 100644 --- a/docs/cmdline-opts/hsts.md +++ b/docs/cmdline-opts/hsts.md @@ -18,11 +18,13 @@ Example: Enable HSTS for the transfer. If the filename points to an existing HSTS cache file, that is used. After a completed transfer, the cache is saved to the -filename again if it has been modified. +filename again if it has been modified. If you run multiple curl invokes at +the same time using the same HSTS cache file, they might interfere with each +other in possibly undesired ways. -If curl is told to use HTTP:// for a transfer involving a hostname that exists -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 +If curl is told to use `http://` for a transfer involving a hostname that +exists 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 diff --git a/docs/cmdline-opts/http1.1.md b/docs/cmdline-opts/http1.1.md index 14e5c74702aa..3d241e5c5944 100644 --- a/docs/cmdline-opts/http1.1.md +++ b/docs/cmdline-opts/http1.1.md @@ -18,4 +18,4 @@ Example: # `--http1.1` -Use HTTP version 1.1. This is the default with HTTP:// URLs. +Use HTTP version 1.1. This is the default with `http://` URLs. diff --git a/docs/cmdline-opts/output.md b/docs/cmdline-opts/output.md index 0c4f7f9facdd..b2d196d038da 100644 --- a/docs/cmdline-opts/output.md +++ b/docs/cmdline-opts/output.md @@ -23,10 +23,10 @@ Example: # `--output` -Write output to the given file instead of stdout. If you are using globbing to -fetch multiple documents, you should quote the URL and you can use `#` -followed by a number in the filename. That variable is then replaced with the -current string for the URL being fetched. Like in: +Write output to the given file instead of stdout. If you are using globbing in +the URL to fetch multiple documents, you should quote the URL and you can use +`#` followed by a number in the filename. That variable gets replaced with the +current glob text. Like in: curl "http://{one,two}.example.com" -o "file_#1.txt" @@ -69,3 +69,22 @@ override curl's internal binary output in terminal prevention: Note that the binary output may be caused by the response being compressed, in which case you may want to use the --compressed option. + +Since curl 8.21.0, the separate globbing parts can be named and referenced by +their names. The case sensitive alphanumeric name is set enclosed within angle +brackets after the opening character. Examples: + + curl "https://fun.example/{one,two}.jpg" -o "save-#" + + curl "ftp://ftp.example/file[1-100].txt" \ + -o "save-#.txt" + +Referencing a named glob that is not set, causes an error. + +Since curl 8.21.0, you can use parts of the upload filename when it uses +globbing by setting a glob name and referencing it the same way you reference +named URL globs. For example, if you upload three files to a single fixed HTTP +URL and want to save the corresponding responses in separate files: + + curl -T 'file{1,2,3}' \ + https://upload.example/ -o 'response-#' diff --git a/docs/cmdline-opts/preproxy.md b/docs/cmdline-opts/preproxy.md index 87d94a9604fc..a108d9f09485 100644 --- a/docs/cmdline-opts/preproxy.md +++ b/docs/cmdline-opts/preproxy.md @@ -20,9 +20,9 @@ Use the specified SOCKS proxy before connecting to an HTTP or HTTPS --proxy. In such a case curl first connects to the SOCKS proxy and then connects (through SOCKS) to the HTTP or HTTPS proxy. Hence pre proxy. -The pre proxy string should be specified with a protocol:// prefix to specify -alternative proxy protocols. Use socks4://, socks4a://, socks5:// or -socks5h:// to request the specific SOCKS version to be used. No protocol +The pre proxy string should be specified with a `protocol://` prefix to specify +alternative proxy protocols. Use `socks4://`, `socks4a://`, `socks5://` or +`socks5h://` to request the specific SOCKS version to be used. No protocol specified makes curl default to SOCKS4. If the port number is not specified in the proxy string, it is assumed to be diff --git a/docs/cmdline-opts/proto-default.md b/docs/cmdline-opts/proto-default.md index 209e5cdc83da..903fac73a5e5 100644 --- a/docs/cmdline-opts/proto-default.md +++ b/docs/cmdline-opts/proto-default.md @@ -16,7 +16,8 @@ Example: # `--proto-default` -Use *protocol* for any provided URL missing a scheme. +Use *protocol* for any provided URL missing a scheme. The case-insensitive +name should be given without any `://` suffix. An unknown or unsupported protocol causes error *CURLE_UNSUPPORTED_PROTOCOL*. @@ -24,3 +25,6 @@ This option does not change the default proxy protocol (http). Without this option set, curl guesses protocol based on the hostname, see --url for details. + +The default protocol cannot be set to `ipfs` or `ipns`. Those schemes need to +be used explicitly in the URL. diff --git a/docs/cmdline-opts/proxy-http2.md b/docs/cmdline-opts/proxy-http2.md index ca6a091f328e..a38da9e87ed8 100644 --- a/docs/cmdline-opts/proxy-http2.md +++ b/docs/cmdline-opts/proxy-http2.md @@ -5,7 +5,7 @@ Long: proxy-http2 Tags: Versions HTTP/2 Protocols: HTTP Added: 8.1.0 -Mutexed: +Mutexed: proxy-http3 Requires: HTTP/2 Help: Use HTTP/2 with HTTPS proxy Category: http proxy @@ -22,3 +22,5 @@ Negotiate HTTP/2 with an HTTPS proxy. The proxy might still only offer HTTP/1 and then curl sticks to using that version. This has no effect for any other kinds of proxies. + +This option is mutually exclusive with `--proxy-http3`. diff --git a/docs/cmdline-opts/proxy-http3.md b/docs/cmdline-opts/proxy-http3.md new file mode 100644 index 000000000000..6533b980b6cc --- /dev/null +++ b/docs/cmdline-opts/proxy-http3.md @@ -0,0 +1,31 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: proxy-http3 +Tags: Versions HTTP/3 +Protocols: HTTP +Added: 8.21.0 +Mutexed: proxy-http2 +Requires: HTTP/3 +Help: Use HTTP/3 with HTTPS proxy +Category: http proxy +Multi: boolean +See-also: + - proxy + - proxy-http2 +Example: + - --proxy-http3 -x proxy $URL +--- + +# `--proxy-http3` + +Negotiate HTTP/3 with an HTTPS proxy. +Fails to perform the transfer if the given proxy does not support HTTP/3. + +This has no effect for any other kinds of proxies. + +This option is mutually exclusive with `--proxy-http2`. + +This feature is experimental and requires a build with HTTP/3 proxy support +enabled. For autotools builds, use `--enable-proxy-http3`. For CMake builds, +use `-DUSE_PROXY_HTTP3=ON`. diff --git a/docs/cmdline-opts/proxy.md b/docs/cmdline-opts/proxy.md index 6cd456169d34..881f62d6a1a8 100644 --- a/docs/cmdline-opts/proxy.md +++ b/docs/cmdline-opts/proxy.md @@ -19,15 +19,15 @@ Example: Use the specified proxy. -The proxy string can be specified with a protocol:// prefix. No protocol -specified or http:// it is treated as an HTTP proxy. Use socks4://, -socks4a://, socks5:// or socks5h:// to request a specific SOCKS version to be -used. (Added in 7.21.7) +The proxy string can be specified with a `protocol://` prefix. No protocol +specified or http:// it is treated as an HTTP proxy. Use `socks4://`, +`socks4a://`, `socks5://` or `socks5h://` to request a specific SOCKS version +to be used. (Added in 7.21.7) Unix domain sockets are supported for socks proxy. Set localhost for the host part. e.g. socks5h://localhost/path/to/socket.sock -HTTPS proxy support works with the https:// protocol prefix for OpenSSL and +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). @@ -50,7 +50,7 @@ by curl. This allows you to pass in special characters such as @ by using %40 or pass in a colon with %3a. The proxy host can be specified the same way as the proxy environment -variables, including the protocol prefix (http://) and the embedded user + +variables, including the protocol prefix (`http://`) and the embedded user + password. When a proxy is used, the active FTP mode as set with --ftp-port, cannot be diff --git a/docs/cmdline-opts/show-headers.md b/docs/cmdline-opts/show-headers.md index d733784ac22d..40c5ae790bec 100644 --- a/docs/cmdline-opts/show-headers.md +++ b/docs/cmdline-opts/show-headers.md @@ -24,6 +24,12 @@ non-HTTP protocols, the "headers" are other server communication. This option makes the response headers get saved in the same stream/output as the data. --dump-header exists to save headers in a separate stream. +When HTTP headers are output to a tty, curl may use escape codes to make the +header field names appear in bold and URLs in `Location:` headers be +especially marked as such. Disable the use of terminal escape codes with +--no-styled-output. (This means using the --styled-output option with a +`--no-` prefix to disable it.) + To view the request headers, consider the --verbose option. Prior to 7.75.0 curl did not print the headers if --fail was used in diff --git a/docs/cmdline-opts/socks4.md b/docs/cmdline-opts/socks4.md index 59ec172b8d86..9d1c5671c939 100644 --- a/docs/cmdline-opts/socks4.md +++ b/docs/cmdline-opts/socks4.md @@ -30,7 +30,7 @@ This option overrides any previous use of --proxy, as they are mutually exclusive. This option is superfluous since you can specify a socks4 proxy with --proxy -using a socks4:// protocol prefix. (Added in 7.21.7) +using a `socks4://` protocol prefix. (Added in 7.21.7) --preproxy can be used to specify a SOCKS proxy at the same time proxy is used with an HTTP/HTTPS proxy (added in 7.52.0). In such a case, curl first diff --git a/docs/cmdline-opts/socks4a.md b/docs/cmdline-opts/socks4a.md index 9e451cf7b063..695e44cb2b39 100644 --- a/docs/cmdline-opts/socks4a.md +++ b/docs/cmdline-opts/socks4a.md @@ -29,7 +29,7 @@ This option overrides any previous use of --proxy, as they are mutually exclusive. This option is superfluous since you can specify a socks4a proxy with --proxy -using a socks4a:// protocol prefix. (Added in 7.21.7) +using a `socks4a://` protocol prefix. (Added in 7.21.7) --preproxy can be used to specify a SOCKS proxy at the same time --proxy is used with an HTTP/HTTPS proxy (added in 7.52.0). In such a case, curl first diff --git a/docs/cmdline-opts/socks5-hostname.md b/docs/cmdline-opts/socks5-hostname.md index b558248a78b9..f8ee9fe13797 100644 --- a/docs/cmdline-opts/socks5-hostname.md +++ b/docs/cmdline-opts/socks5-hostname.md @@ -28,7 +28,7 @@ This option overrides any previous use of --proxy, as they are mutually exclusive. This option is superfluous since you can specify a socks5 hostname proxy with ---proxy using a socks5h:// protocol prefix. (Added in 7.21.7) +--proxy using a `socks5h://` protocol prefix. (Added in 7.21.7) --preproxy can be used to specify a SOCKS proxy at the same time --proxy is used with an HTTP/HTTPS proxy (added in 7.52.0). In such a case, curl first diff --git a/docs/cmdline-opts/socks5.md b/docs/cmdline-opts/socks5.md index 3aa65b33adda..b70e88f6fa2a 100644 --- a/docs/cmdline-opts/socks5.md +++ b/docs/cmdline-opts/socks5.md @@ -29,7 +29,7 @@ This option overrides any previous use of --proxy, as they are mutually exclusive. This option is superfluous since you can specify a socks5 proxy with --proxy -using a socks5:// protocol prefix. (Added in 7.21.7) +using a `socks5://` protocol prefix. (Added in 7.21.7) --preproxy can be used to specify a SOCKS proxy at the same time --proxy is used with an HTTP/HTTPS proxy (added in 7.52.0). In such a case, curl first diff --git a/docs/cmdline-opts/ssl.md b/docs/cmdline-opts/ssl.md index 5951d0199123..b2345858091d 100644 --- a/docs/cmdline-opts/ssl.md +++ b/docs/cmdline-opts/ssl.md @@ -30,5 +30,7 @@ OpenLDAP backend and ignored by the generic ldap backend. Please note that a server may close the connection if the negotiation fails. +If set, this option overrides --ftp-ssl-control. + 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/tls-earlydata.md b/docs/cmdline-opts/tls-earlydata.md index 8e344758be5e..22a7abd3c376 100644 --- a/docs/cmdline-opts/tls-earlydata.md +++ b/docs/cmdline-opts/tls-earlydata.md @@ -20,8 +20,8 @@ Example: 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). +This option can be used when curl is built to use GnuTLS, OpenSSL, quictls and +wolfSSL as a TLS provider (but not AWS-LC, BoringSSL, 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/upload-file.md b/docs/cmdline-opts/upload-file.md index 5a2842e58ad8..1988d8afb728 100644 --- a/docs/cmdline-opts/upload-file.md +++ b/docs/cmdline-opts/upload-file.md @@ -26,13 +26,13 @@ Upload the specified local file to the remote URL. If there is no file part in the specified URL, curl appends the local file name to the end of the URL before the operation starts. You must use a -trailing slash (/) on the last directory to prove to curl that there is no +trailing slash (`/`) on the last directory to prove to curl that there is no filename or curl thinks that your last directory name is the remote filename to use. When putting the local filename at the end of the URL, curl ignores what is on -the left side of any slash (/) or backslash (\\) used in the filename and only -appends what is on the right side of the rightmost such character. +the left side of any slash (`/`) or backslash (`\\`) used in the filename and +only appends what is on the right side of the rightmost such character. Use the filename `-` (a single dash) to use stdin instead of a given file. Alternately, the filename `.` (a single period) may be specified instead of @@ -45,9 +45,19 @@ You can specify one --upload-file for each URL on the command line. Each --upload-file + URL pair specifies what to upload and to where. curl also supports globbing of the --upload-file argument, meaning that you can upload multiple files to a single URL by using the same URL globbing style supported -in the URL. +in the URL. Example: -When uploading to an SMTP server: the uploaded data is assumed to be RFC 5322 -formatted. It has to feature the necessary set of headers and mail body -formatted correctly by the user as curl does not transcode nor encode it -further in any way. + curl --upload-file 'file{1,2,3}' ftp://ftp.example/ + +Since curl 8.21.0, you can use parts of the upload filename when it uses +globbing by setting a glob name and referencing that in the same way you +reference named URL globs. For example, if you upload three files to a single +fixed HTTP URL and want to save the corresponding responses in separate files: + + curl -T 'file{1,2,3}' \ + https://upload.example/ -o 'response-#' + +When uploading to an SMTP server (aka "sending email"): the uploaded data is +assumed to be RFC 5322 formatted. It has to feature the necessary set of +headers and mail body formatted correctly by the user as curl does not +transcode nor encode it further in any way. diff --git a/docs/cmdline-opts/user-agent.md b/docs/cmdline-opts/user-agent.md index a24bd28449ad..d81b65d34fa4 100644 --- a/docs/cmdline-opts/user-agent.md +++ b/docs/cmdline-opts/user-agent.md @@ -19,8 +19,8 @@ Example: # `--user-agent` Specify the User-Agent string to send to the HTTP server. To encode blanks in -the string, surround the string with single quote marks. This header can also -be set with the --header or the --proxy-header options. +the string, surround the string with single or double quote marks. This header +can also be set with the --header or the --proxy-header options. If you give an empty argument to --user-agent (""), it removes the header completely from the request. If you prefer a blank header, you can set it to a diff --git a/docs/cmdline-opts/write-out.md b/docs/cmdline-opts/write-out.md index df0d3d5c84f1..5cf9d91f8704 100644 --- a/docs/cmdline-opts/write-out.md +++ b/docs/cmdline-opts/write-out.md @@ -202,7 +202,8 @@ The total amount of bytes that were downloaded. This is the size of the body/data that was transferred, excluding headers. ## `size_header` -The total amount of bytes of the downloaded headers. +The total amount of bytes of the downloaded headers, as represented in +HTTP/1-style header format. ## `size_request` The total amount of bytes that were sent in the HTTP request. diff --git a/docs/examples/10-at-a-time.c b/docs/examples/10-at-a-time.c index f04702f59b8b..d0982e0d4118 100644 --- a/docs/examples/10-at-a-time.c +++ b/docs/examples/10-at-a-time.c @@ -134,14 +134,14 @@ int main(void) 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); + fprintf(stderr, "R: %d - %s <%s>\n", (int)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); + fprintf(stderr, "E: CURLMsg (%d)\n", (int)msg->msg); } if(transfers < NUM_URLS) add_transfer(multi, transfers++, &left); diff --git a/docs/examples/adddocsref.pl b/docs/examples/adddocsref.pl index cbc48c040716..8c9d7f8c20bf 100755 --- a/docs/examples/adddocsref.pl +++ b/docs/examples/adddocsref.pl @@ -30,7 +30,7 @@ use File::Copy; -my $docroot="https://curl.se/libcurl/c"; +my $docroot = "https://curl.se/libcurl/c"; for my $f (@ARGV) { open(NEW, ">$f.new"); diff --git a/docs/examples/anyauthput.c b/docs/examples/anyauthput.c index 9b08bbe41ba2..b1bd9f0fbf2f 100644 --- a/docs/examples/anyauthput.c +++ b/docs/examples/anyauthput.c @@ -109,7 +109,7 @@ int main(int argc, const char **argv) if(!fp) return 2; - if(fstat(fileno(fp), &file_info) != 0) { + if(fstat(fileno(fp), &file_info)) { fclose(fp); return 1; /* cannot continue */ } diff --git a/docs/examples/cacertinmem.c b/docs/examples/cacertinmem.c index 8ede167c2819..2e43a783f5dc 100644 --- a/docs/examples/cacertinmem.c +++ b/docs/examples/cacertinmem.c @@ -155,7 +155,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_CAINFO, NULL); curl_easy_setopt(curl, CURLOPT_CAPATH, NULL); - /* first try: retrieve page without ca certificates -> should fail + /* 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); @@ -166,10 +166,10 @@ int main(void) /* 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 + * 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 + * 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); diff --git a/docs/examples/chkspeed.c b/docs/examples/chkspeed.c index 4274eb0c9051..7010acc4aeca 100644 --- a/docs/examples/chkspeed.c +++ b/docs/examples/chkspeed.c @@ -105,7 +105,7 @@ int main(int argc, const char *argv[]) case 'm': case 'M': if(argv[0][2] == '=') { - int m = atoi((*argv) + 3); + int m = atoi(*argv + 3); switch(m) { case 1: url = URL_1M; diff --git a/docs/examples/crawler.c b/docs/examples/crawler.c index 04d816320435..21cdb5fcff4c 100644 --- a/docs/examples/crawler.c +++ b/docs/examples/crawler.c @@ -173,7 +173,7 @@ static size_t follow_links(CURLM *multi, struct memory *mem, const char *url) static int is_html(const char *ctype) { - return ctype != NULL && strlen(ctype) > 10 && strstr(ctype, "text/html"); + return ctype && strlen(ctype) > 10 && strstr(ctype, "text/html"); } int main(void) diff --git a/docs/examples/ephiperfifo.c b/docs/examples/ephiperfifo.c index 62c3b2c674c8..292bfc741ee5 100644 --- a/docs/examples/ephiperfifo.c +++ b/docs/examples/ephiperfifo.c @@ -42,7 +42,7 @@ * curl_multi "hiper" API. * * Thus, you can try a single URL: - * % echo http://www.yahoo.com > hiper.fifo + * % echo http://www.example.com > hiper.fifo * * Or a whole bunch of them: * % cat my-url-list > hiper.fifo @@ -412,7 +412,7 @@ static int init_fifo(struct GlobalInfo *g) struct epoll_event epev; fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo); - if(lstat(fifo, &st) == 0) { + if(!lstat(fifo, &st)) { if((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); diff --git a/docs/examples/evhiperfifo.c b/docs/examples/evhiperfifo.c index e4fdf7d08929..079eab1e8abf 100644 --- a/docs/examples/evhiperfifo.c +++ b/docs/examples/evhiperfifo.c @@ -45,7 +45,7 @@ * curl_multi "hiper" API. * * Thus, you can try a single URL: - * % echo http://www.yahoo.com > hiper.fifo + * % echo http://www.example.com > hiper.fifo * * Or a whole bunch of them: * % cat my-url-list > hiper.fifo @@ -173,7 +173,7 @@ static void timer_cb(EV_P_ struct ev_timer *w, int revents) CURLMcode mresult; struct GlobalInfo *g; - printf("%s w %p revents %i\n", __PRETTY_FUNCTION__, (void *)w, revents); + printf("%s w %p revents %d\n", __PRETTY_FUNCTION__, (void *)w, revents); g = (struct GlobalInfo *)w->data; @@ -207,7 +207,7 @@ static void event_cb(EV_P_ struct ev_io *w, int revents) 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); + printf("%s w %p revents %d\n", __PRETTY_FUNCTION__, (void *)w, revents); g = (struct GlobalInfo *)w->data; mresult = curl_multi_socket_action(g->multi, w->fd, action, @@ -269,7 +269,7 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) 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", + printf("%s e %p s %d what %d 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]); @@ -382,7 +382,7 @@ static int init_fifo(struct GlobalInfo *g) curl_socket_t sockfd; fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo); - if(lstat(fifo, &st) == 0) { + if(!lstat(fifo, &st)) { if((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); diff --git a/docs/examples/externalsocket.c b/docs/examples/externalsocket.c index 1d74ade723b7..b07f48b44724 100644 --- a/docs/examples/externalsocket.c +++ b/docs/examples/externalsocket.c @@ -164,7 +164,7 @@ int main(void) close(sockfd); if(result != CURLE_OK) { - printf("libcurl error: %d\n", result); + printf("libcurl error: %d\n", (int)result); return 4; } } diff --git a/docs/examples/fileupload.c b/docs/examples/fileupload.c index f444da9b9e58..0c2b3a57af5a 100644 --- a/docs/examples/fileupload.c +++ b/docs/examples/fileupload.c @@ -64,7 +64,7 @@ int main(void) } /* to get the file size */ - if(fstat(fileno(fd), &file_info) != 0) { + if(fstat(fileno(fd), &file_info)) { fclose(fd); curl_global_cleanup(); return 1; /* cannot continue */ diff --git a/docs/examples/ftp-delete.c b/docs/examples/ftp-delete.c index 5e49e5f8f7b6..c05f2f7a5457 100644 --- a/docs/examples/ftp-delete.c +++ b/docs/examples/ftp-delete.c @@ -74,7 +74,7 @@ int main(void) if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } } diff --git a/docs/examples/ftpget.c b/docs/examples/ftpget.c index 973049e78ba3..eec6c86348fe 100644 --- a/docs/examples/ftpget.c +++ b/docs/examples/ftpget.c @@ -87,7 +87,7 @@ int main(void) if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } } diff --git a/docs/examples/ftpgetinfo.c b/docs/examples/ftpgetinfo.c index b943f48d0285..d35cc47b5667 100644 --- a/docs/examples/ftpgetinfo.c +++ b/docs/examples/ftpgetinfo.c @@ -90,7 +90,7 @@ int main(void) } else { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } /* always cleanup */ diff --git a/docs/examples/ftpsget.c b/docs/examples/ftpsget.c index abe1d40fda17..44d8665325d0 100644 --- a/docs/examples/ftpsget.c +++ b/docs/examples/ftpsget.c @@ -69,7 +69,7 @@ int main(void) if(curl) { /* * You better replace the URL with one that works! Note that we use an - * FTP:// URL with standard explicit FTPS. You can also do FTPS:// URLs if + * ftp:// URL with standard explicit FTPS. You can also do ftps:// URLs if * you want to do the rarer kind of transfers: implicit. */ curl_easy_setopt(curl, CURLOPT_URL, @@ -92,7 +92,7 @@ int main(void) if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } } diff --git a/docs/examples/ftpupload.c b/docs/examples/ftpupload.c index 415bafe68ee2..07546f313e90 100644 --- a/docs/examples/ftpupload.c +++ b/docs/examples/ftpupload.c @@ -96,7 +96,7 @@ int main(void) } /* to get the file size */ - if(fstat(fileno(hd_src), &file_info) != 0) { + if(fstat(fileno(hd_src), &file_info)) { fclose(hd_src); return 1; /* cannot continue */ } diff --git a/docs/examples/ghiper.c b/docs/examples/ghiper.c index 31db2bc9f9d1..fd6041761e24 100644 --- a/docs/examples/ghiper.c +++ b/docs/examples/ghiper.c @@ -42,7 +42,7 @@ * curl_multi "hiper" API. * * Thus, you can try a single URL: - * % echo http://www.yahoo.com > hiper.fifo + * % echo http://www.example.com > hiper.fifo * * Or a whole bunch of them: * % cat my-url-list > hiper.fifo @@ -399,7 +399,7 @@ int init_fifo(void) const char *fifo = "hiper.fifo"; int socket; - if(lstat(fifo, &st) == 0) { + if(!lstat(fifo, &st)) { if((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); diff --git a/docs/examples/hiperfifo.c b/docs/examples/hiperfifo.c index 2b0ae0fdb964..96719530cf46 100644 --- a/docs/examples/hiperfifo.c +++ b/docs/examples/hiperfifo.c @@ -42,7 +42,7 @@ * curl_multi "hiper" API. * * Thus, you can try a single URL: - * % echo http://www.yahoo.com > hiper.fifo + * % echo http://www.example.com > hiper.fifo * * Or a whole bunch of them: * % cat my-url-list > hiper.fifo @@ -383,7 +383,7 @@ static int init_fifo(struct GlobalInfo *g) curl_socket_t sockfd; fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo); - if(lstat(fifo, &st) == 0) { + if(!lstat(fifo, &st)) { if((st.st_mode & S_IFMT) == S_IFREG) { errno = EEXIST; perror("lstat"); diff --git a/docs/examples/htmltitle.cpp b/docs/examples/htmltitle.cpp index 7986b94640a9..cce6d16e11a1 100644 --- a/docs/examples/htmltitle.cpp +++ b/docs/examples/htmltitle.cpp @@ -73,7 +73,7 @@ static std::string buffer; static size_t writer(char *data, size_t size, size_t nmemb, std::string *writerData) { - if(writerData == NULL) + if(!writerData) return 0; writerData->append(data, size * nmemb); diff --git a/docs/examples/http2-download.c b/docs/examples/http2-download.c index 80c3365bb255..3d612781bbfe 100644 --- a/docs/examples/http2-download.c +++ b/docs/examples/http2-download.c @@ -176,7 +176,7 @@ static int setup(struct transfer *t, int num) /* HTTP/2 please */ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); -#if (CURLPIPE_MULTIPLEX > 0) +#if CURLPIPE_MULTIPLEX > 0 /* wait for pipe connection to confirm */ curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L); #endif diff --git a/docs/examples/http2-upload.c b/docs/examples/http2-upload.c index aeac13ca2b22..cad74afc50de 100644 --- a/docs/examples/http2-upload.c +++ b/docs/examples/http2-upload.c @@ -234,7 +234,7 @@ static int setup(struct input *t, int num, const char *upload) return 1; } - if(fstat(fileno(t->in), &file_info) != 0) { + if(fstat(fileno(t->in), &file_info)) { fprintf(stderr, "error: could not stat file %s: %s\n", upload, strerror(errno)); fclose(t->out); @@ -275,7 +275,7 @@ static int setup(struct input *t, int num, const char *upload) curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); -#if (CURLPIPE_MULTIPLEX > 0) +#if CURLPIPE_MULTIPLEX > 0 /* wait for pipe connection to confirm */ curl_easy_setopt(curl, CURLOPT_PIPEWAIT, 1L); #endif diff --git a/docs/examples/httpput-postfields.c b/docs/examples/httpput-postfields.c index caa0acfae51f..602190d9f6c1 100644 --- a/docs/examples/httpput-postfields.c +++ b/docs/examples/httpput-postfields.c @@ -40,7 +40,7 @@ static const char olivertwist[] = "all events; the item of mortality whose name is prefixed"; /* ... to the head of this chapter. String cut off to stick within the C90 - 509 byte limit. */ + 509-byte limit. */ /* * This example shows an HTTP PUT operation that sends a fixed buffer with diff --git a/docs/examples/httpput.c b/docs/examples/httpput.c index 977d31ccaf29..fb33c8ac406a 100644 --- a/docs/examples/httpput.c +++ b/docs/examples/httpput.c @@ -96,7 +96,7 @@ int main(int argc, const char **argv) return 2; /* get the file size of the local file */ - if(fstat(fileno(hd_src), &file_info) != 0) { + if(fstat(fileno(hd_src), &file_info)) { fclose(hd_src); return 1; /* cannot continue */ } diff --git a/docs/examples/log_failed_transfers.c b/docs/examples/log_failed_transfers.c index 3279703176db..564735e30dd8 100644 --- a/docs/examples/log_failed_transfers.c +++ b/docs/examples/log_failed_transfers.c @@ -289,7 +289,7 @@ int main(void) failed = 0; } else { - mem_addf(&t->log, "Transfer failed: (%d) %s\n", result, + mem_addf(&t->log, "Transfer failed: (%d) %s\n", (int)result, (errbuf[0] ? errbuf : curl_easy_strerror(result))); fprintf(stderr, "%s", t->log.recent); failed = 1; diff --git a/docs/examples/multi-app.c b/docs/examples/multi-app.c index 4a1f3b1f979a..56509736e878 100644 --- a/docs/examples/multi-app.c +++ b/docs/examples/multi-app.c @@ -99,10 +99,12 @@ int main(void) switch(idx) { case HTTP_HANDLE: - printf("HTTP transfer completed with status %d\n", msg->data.result); + printf("HTTP transfer completed with status %d\n", + (int)msg->data.result); break; case FTP_HANDLE: - printf("FTP transfer completed with status %d\n", msg->data.result); + printf("FTP transfer completed with status %d\n", + (int)msg->data.result); break; } } diff --git a/docs/examples/multi-event.c b/docs/examples/multi-event.c index 4c52cbe3b3ad..58744b6e2e6f 100644 --- a/docs/examples/multi-event.c +++ b/docs/examples/multi-event.c @@ -99,9 +99,7 @@ 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)); + struct curl_context *context = malloc(sizeof(*context)); context->sockfd = sockfd; diff --git a/docs/examples/multi-legacy.c b/docs/examples/multi-legacy.c index a0580c6712e2..bd23866d6f8c 100644 --- a/docs/examples/multi-legacy.c +++ b/docs/examples/multi-legacy.c @@ -177,10 +177,12 @@ int main(void) switch(idx) { case HTTP_HANDLE: - printf("HTTP transfer completed with status %d\n", msg->data.result); + printf("HTTP transfer completed with status %d\n", + (int)msg->data.result); break; case FTP_HANDLE: - printf("FTP transfer completed with status %d\n", msg->data.result); + printf("FTP transfer completed with status %d\n", + (int)msg->data.result); break; } } diff --git a/docs/examples/multi-uv.c b/docs/examples/multi-uv.c index 8d6227fc7e78..094df35105ca 100644 --- a/docs/examples/multi-uv.c +++ b/docs/examples/multi-uv.c @@ -58,9 +58,7 @@ struct curl_context { static struct curl_context *create_curl_context(curl_socket_t sockfd, struct datauv *uv) { - struct curl_context *context; - - context = (struct curl_context *)malloc(sizeof(*context)); + struct curl_context *context = malloc(sizeof(*context)); context->sockfd = sockfd; context->uv = uv; diff --git a/docs/examples/sftpget.c b/docs/examples/sftpget.c index 53bc81ea6dad..a3da2d672191 100644 --- a/docs/examples/sftpget.c +++ b/docs/examples/sftpget.c @@ -103,7 +103,7 @@ int main(void) if(result != CURLE_OK) { /* we failed */ - fprintf(stderr, "curl told us %d\n", result); + fprintf(stderr, "curl told us %d\n", (int)result); } } diff --git a/docs/examples/simplessl.c b/docs/examples/simplessl.c index f1c07a4ae239..43fe34cf3fbf 100644 --- a/docs/examples/simplessl.c +++ b/docs/examples/simplessl.c @@ -22,7 +22,7 @@ * ***************************************************************************/ /* - * Shows HTTPS usage with client certs and optional ssl engine use. + * Shows HTTPS usage with client certs and optional SSL engine use. * */ #ifdef _MSC_VER diff --git a/docs/examples/smooth-gtk-thread.c b/docs/examples/smooth-gtk-thread.c index 06eea1ff0e64..82ce1a2256de 100644 --- a/docs/examples/smooth-gtk-thread.c +++ b/docs/examples/smooth-gtk-thread.c @@ -125,7 +125,7 @@ static void *create_thread(void *progress_bar) pthread_t tid[NUMT]; int i; - /* Make sure I do not create more threads than urls. */ + /* Make sure I do not create more threads than URLs. */ for(i = 0; i < NUMT && i < num_urls; i++) { int error = pthread_create(&tid[i], NULL, /* default attributes please */ @@ -210,7 +210,7 @@ int main(int argc, const char **argv) 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) + if(!g_thread_create(&create_thread, progress_bar, FALSE, NULL)) g_warning("cannot create the thread"); gtk_main(); diff --git a/docs/examples/sslbackend.c b/docs/examples/sslbackend.c index e10eaaa21754..20002c4f816a 100644 --- a/docs/examples/sslbackend.c +++ b/docs/examples/sslbackend.c @@ -22,7 +22,7 @@ * ***************************************************************************/ /* - * Shows HTTPS usage with client certs and optional ssl engine use. + * Shows HTTPS usage with client certs and optional SSL engine use. * */ #include @@ -54,7 +54,7 @@ int main(int argc, const char **argv) for(i = 0; list[i]; i++) printf("SSL backend #%d: '%s' (ID: %d)\n", - i, list[i]->name, list[i]->id); + i, list[i]->name, (int)list[i]->id); return 0; } diff --git a/docs/examples/synctime.c b/docs/examples/synctime.c index ed10f9d3b816..1157edbe3fa0 100644 --- a/docs/examples/synctime.c +++ b/docs/examples/synctime.c @@ -149,7 +149,7 @@ static size_t SyncTime_CURL_WriteHeader(void *ptr, size_t size, size_t nmemb, int i; SYSTime.wMilliseconds = 500; /* adjust to midpoint, 0.5 sec */ for(i = 0; i < 12; i++) { - if(strcmp(MthStr[i], TmpStr2) == 0) { + if(!strcmp(MthStr[i], TmpStr2)) { SYSTime.wMonth = (WORD)(i + 1); break; } @@ -232,26 +232,26 @@ int main(int argc, const char *argv[]) if(argc > 1) { int OptionIndex = 1; while(OptionIndex < argc) { - if(strncmp(argv[OptionIndex], "--server=", 9) == 0) + if(!strncmp(argv[OptionIndex], "--server=", 9)) snprintf(conf.timeserver, sizeof(conf.timeserver) - 1, "%s", &argv[OptionIndex][9]); - if(strcmp(argv[OptionIndex], "--showall") == 0) + if(!strcmp(argv[OptionIndex], "--showall")) ShowAllHeader = 1; - if(strcmp(argv[OptionIndex], "--synctime") == 0) + if(!strcmp(argv[OptionIndex], "--synctime")) AutoSyncTime = 1; - if(strncmp(argv[OptionIndex], "--proxy-user=", 13) == 0) + if(!strncmp(argv[OptionIndex], "--proxy-user=", 13)) snprintf(conf.proxy_user, sizeof(conf.proxy_user) - 1, "%s", &argv[OptionIndex][13]); - if(strncmp(argv[OptionIndex], "--proxy=", 8) == 0) + if(!strncmp(argv[OptionIndex], "--proxy=", 8)) snprintf(conf.http_proxy, sizeof(conf.http_proxy) - 1, "%s", &argv[OptionIndex][8]); - if((strcmp(argv[OptionIndex], "--help") == 0) || - (strcmp(argv[OptionIndex], "/?") == 0)) { + if(!strcmp(argv[OptionIndex], "--help") || + !strcmp(argv[OptionIndex], "/?")) { showUsage(); return 0; } diff --git a/docs/examples/version-check.pl b/docs/examples/version-check.pl index e6ad784fc811..125c3b5f7eb2 100755 --- a/docs/examples/version-check.pl +++ b/docs/examples/version-check.pl @@ -41,21 +41,21 @@ use strict; use warnings; -open(S, "<../libcurl/symbols-in-versions") || die; +open(S, "<../libcurl/symbols-in-versions") or die; my %doc; my %rem; while() { if(/(^CURL[^ \n]*) *(.*)/) { - my ($sym, $rest)=($1, $2); - my @a=split(/ +/, $rest); + my ($sym, $rest) = ($1, $2); + my @a = split(/ +/, $rest); - $doc{$sym}=$a[0]; # when it was introduced + $doc{$sym} = $a[0]; # when it was introduced if($a[2]) { # this symbol is documented to have been present the last time # in this release - $rem{$sym}=$a[2]; + $rem{$sym} = $a[2]; } } } @@ -63,14 +63,14 @@ close(S); sub age { - my ($ver)=@_; + my ($ver) = @_; - my @s=split(/\./, $ver); + my @s = split(/\./, $ver); return $s[0]*10000+$s[1]*100+($s[2] || 0); } my %used; -open(C, "<$ARGV[0]") || die; +open(C, "<$ARGV[0]") or die; while() { if(/\W(CURL[_A-Z0-9v]+)\W/) { diff --git a/docs/examples/websocket-updown.c b/docs/examples/websocket-updown.c index 3000be059497..85a7e74ea397 100644 --- a/docs/examples/websocket-updown.c +++ b/docs/examples/websocket-updown.c @@ -69,7 +69,7 @@ static size_t read_cb(char *buf, size_t nitems, size_t buflen, void *p) result = curl_ws_start_frame(ctx->curl, CURLWS_TEXT, (curl_off_t)ctx->blen); if(result != CURLE_OK) { - fprintf(stderr, "error starting frame: %d\n", result); + fprintf(stderr, "error starting frame: %d\n", (int)result); return CURL_READFUNC_ABORT; } } diff --git a/docs/examples/websocket.c b/docs/examples/websocket.c index ec445d88ed2d..953c2f358fcf 100644 --- a/docs/examples/websocket.c +++ b/docs/examples/websocket.c @@ -94,7 +94,7 @@ static CURLcode recv_pong(CURL *curl, const char *expected_payload) else { /* some other frame arrived. */ fprintf(stderr, "ws: received frame of %u bytes rflags %x\n", - (unsigned int)rlen, meta->flags); + (unsigned int)rlen, (unsigned int)meta->flags); goto retry; } } diff --git a/docs/internals/BUFQ.md b/docs/internals/BUFQ.md index 9d926537d66a..a977fcc8bdc1 100644 --- a/docs/internals/BUFQ.md +++ b/docs/internals/BUFQ.md @@ -15,26 +15,31 @@ 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. -- writing to a full `q` returns -1 and set *err to CURLE_AGAIN - -ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, CURLcode *err); +CURLcode Curl_bufq_write(struct bufq *q, + const uint8_t *buf, size_t len, + size_t *pnwritten); +``` -- returns the length read from `q` or -1 on error. -- reading from an empty `q` returns -1 and set *err to CURLE_AGAIN +- sets `pnwritten` to the length written into `q` or -1 on error. +- writing to a full `q` sets `pnwritten` to -1 and returns CURLE_AGAIN ``` +CURLcode Curl_bufq_read(struct bufq *q, uint8_t *buf, size_t len, + size_t *pnread); +``` + +- sets `pnread` to the length read from `q` or -1 on error. +- reading from an empty `q` sets `pnread` to -1 and returns CURLE_AGAIN 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); +typedef CURLcode Curl_bufq_reader(void *reader_ctx, + uint8_t *buf, size_t len, + size_t *pnread); -ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, void *reader_ctx, - CURLcode *err); +CURLcode Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, + void *reader_ctx, size_t *pnread); ``` `Curl_bufq_slurp()` invokes the given `reader` callback, passing it its own @@ -46,11 +51,12 @@ 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); +typedef CURLcode Curl_bufq_writer(void *writer_ctx, + const uint8_t *buf, size_t len, + size_t *pwritten); -ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, void *writer_ctx, - CURLcode *err); +CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, + void *writer_ctx, size_t *pwritten); ``` `Curl_bufq_pass()` invokes the `writer`, passing its internal memory and @@ -61,7 +67,8 @@ 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); +bool Curl_bufq_peek(struct bufq *q, + const uint8_t **pbuf, size_t *plen); ``` On returning TRUE, `pbuf` points to internal memory with `plen` bytes that one @@ -156,9 +163,11 @@ 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_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); +void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool, + size_t max_chunks, int opts); ``` The pool gets the size and the mount of spares to keep. The `bufq` gets the diff --git a/docs/internals/CHECKSRC.md b/docs/internals/CHECKSRC.md index ea6f260cf13b..4719dd6ba6f5 100644 --- a/docs/internals/CHECKSRC.md +++ b/docs/internals/CHECKSRC.md @@ -40,21 +40,21 @@ warnings are: code style mandates the assignment to be done outside of it. - `ASTERISKNOSPACE`: A pointer was declared like `char* name` instead of the - more appropriate `char *name` style. The asterisk should sit next to the - name. + more appropriate `char *name` style. The asterisk should sit next to the + name. - `ASTERISKSPACE`: A pointer was declared like `char * name` instead of the - more appropriate `char *name` style. The asterisk should sit right next to - the name without a space in between. + more appropriate `char *name` style. The asterisk should sit right next to + the name without a space in between. - `BADCOMMAND`: There is a bad `checksrc` instruction in the code. See the - **Ignore certain warnings** section below for details. + **Ignore certain warnings** section below for details. - `BANNEDFUNC`: A banned function was used. The functions sprintf, vsprintf, - strcat, strncat, gets are **never** allowed in curl source code. + strcat, strncat, gets are **never** allowed in curl source code. - `BRACEELSE`: '} else' on the same line. The else is supposed to be on the - following line. + following line. - `BRACEPOS`: wrong position for an open brace (`{`). @@ -80,8 +80,8 @@ warnings are: 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 - indentations. + warning only checks some specific places and can certainly miss many bad + indentations. - `LONGLINE`: A line is longer than 79 columns. @@ -99,7 +99,7 @@ warnings are: - `PARENBRACE`: `){` was used without sufficient space in between. - `RETURNNOSPACE`: `return` was used without space between the keyword and the - following value. + following value. - `SEMINOSPACE`: There was no space (or newline) following a semicolon. @@ -107,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 `curl_msnprintf()`. + with a different return code etc, we prefer `curl_msnprintf()`. - `SPACEAFTERPAREN`: there was a space after open parenthesis, `( text`. @@ -116,7 +116,7 @@ warnings are: - `SPACEBEFORECOMMA`: there was a space before a comma, `one , two`. - `SPACEBEFOREPAREN`: there was a space before an open parenthesis, `if (`, - where one was not expected + where one was not expected - `SPACESEMICOLON`: there was a space before semicolon, ` ;`. @@ -127,7 +127,11 @@ warnings are: - `TYPEDEFSTRUCT`: we frown upon (most) typedefed structs - `UNUSEDIGNORE`: a `checksrc` inlined warning ignore was asked for but not - used, that is an ignore that should be removed or changed to get used. + used, that is an ignore that should be removed or changed to get used. + +- `USESAFEFREE`: there was a `curlx_free(var)` call made right before assigning + NULL to `var`. We prefer replacing that with `curlx_safefree()`, which is + doing these two operations in a single call. ### Extended warnings @@ -140,7 +144,7 @@ so: `enable ` Currently these are the extended warnings which can be enabled: - `COPYRIGHTYEAR`: the current changeset has not updated the copyright year in - the source file + the source file - `STRERROR`: use of banned function strerror() diff --git a/docs/internals/CONNECTION-FILTERS.md b/docs/internals/CONNECTION-FILTERS.md index 619ca0e3407d..1a817a15672a 100644 --- a/docs/internals/CONNECTION-FILTERS.md +++ b/docs/internals/CONNECTION-FILTERS.md @@ -156,9 +156,9 @@ The currently existing filter types (curl 8.5.0) are: `accept()`ed in a `listen()` * `SSL`: filter that applies TLS en-/decryption and handshake. Manages the underlying TLS backend implementation. -* `HTTP-PROXY`, `H1-PROXY`, `H2-PROXY`: the first manages the connection to an - HTTP proxy server and uses the other depending on which ALPN protocol has - been negotiated. +* `HTTP-PROXY`, `H1-PROXY`, `H2-PROXY`, `H3-PROXY`: the first manages the + connection to an HTTP proxy server and uses the other depending on which + ALPN protocol has been negotiated. * `SOCKS-PROXY`: filter for the various SOCKS proxy protocol variations * `HAPROXY`: filter for the protocol of the same name, providing client IP information to a server. @@ -166,7 +166,7 @@ The currently existing filter types (curl 8.5.0) are: connection * `HTTP/3`: filter for handling multiplexed transfers over an HTTP/3+QUIC connection -* `HAPPY-EYEBALLS`: meta filter that implements IPv4/IPv6 "happy eyeballing". +* `HAPPY-EYEBALLS`: meta filter that implements IPv4/IPv6 "happy eyeballs". It creates up to 2 sub-filters that race each other for a connection. * `SETUP`: meta filter that manages the creation of sub-filter chains for a specific transport (e.g. TCP or QUIC). @@ -220,6 +220,37 @@ as an `SSL` flagged filter is seen first. `conn3` is also encrypted as the Similar checks can determine if a connection is multiplexed or not. +## Adding CONNECT-UDP support +HTTP/3 on top of HTTP/1.1 (MASQUE CONNECT-UDP): +``` +conn --> HTTP/3 --> CAPSULE --> HTTP-PROXY --> H1-PROXY --> SSL --> HAPPY-EYEBALLS --> TCP +``` + +HTTP/3 on top of HTTP/2 (MASQUE CONNECT-UDP): +``` +conn --> HTTP/3 --> CAPSULE --> HTTP-PROXY --> H2-PROXY --> SSL --> HAPPY-EYEBALLS --> TCP +``` + +The CAPSULE filter handles RFC 9297 capsule protocol encapsulation and +decapsulation of UDP datagrams. It is inserted automatically when the +HTTP-PROXY filter completes a successful CONNECT-UDP tunnel. + +## Adding H3-PROXY support +HTTP/1.1 on top of HTTP/3 (CONNECT over QUIC): +``` +conn --> HTTP/1.1 --> SSL --> HTTP-PROXY --> H3-PROXY --> HAPPY-EYEBALLS --> UDP +``` + +HTTP/2 on top of HTTP/3 (CONNECT over QUIC): +``` +conn --> HTTP/2 --> SSL --> HTTP-PROXY --> H3-PROXY --> HAPPY-EYEBALLS --> UDP +``` + +HTTP/3 on top of HTTP/3 (MASQUE CONNECT-UDP over QUIC): +``` +conn --> HTTP/3 --> CAPSULE --> HTTP-PROXY --> H3-PROXY --> HAPPY-EYEBALLS --> UDP +``` + ## Filter Tracing Filters may make use of special trace macros like `CURL_TRC_CF(data, cf, msg, diff --git a/docs/internals/CREDENTIALS.md b/docs/internals/CREDENTIALS.md new file mode 100644 index 000000000000..e5e409ed7bd1 --- /dev/null +++ b/docs/internals/CREDENTIALS.md @@ -0,0 +1,76 @@ + + +# curl `creds` + +Authorization credentials are kept in `struct Curl_creds`. This contains: + +* `user`: the username, maybe the empty string +* `passwd`: the password, maybe the empty string +* `sasl_authzid`: the SASL `authz` value, maybe the empty string +* `oauth_bearer`: the OAUTH bearer token, maybe the empty string +* `source`: where the credentials from +* `refcount`: a reference counter to link/unlink `creds` + +A `creds` with all values empty is equivalent to NULL, e.g. no `creds` +instance. With reference counting, `creds` can be linked in several places. + +Two `creds` are the same if all values are equal apart from `source` +and `refcount`. The comparison of strings is done via `Curl_timestrcmp()` +to prevent side channel attacks. + +## `creds` locations + +Credentials are kept in three places: + +* `data->state.creds`: the credentials to use for the transfer in talking + to the `origin` (see PEERS) +* `conn->creds`: the credentials tied to a connection (more below) +* `conn->*_proxy.creds`: credentials used to talk to the `conn->*_proxy.peer` + +### `data->state.creds` + +This `creds` instance is created when the transfer starts looking for a +suitable connection. For an `easy_perform()` this may happen several times +if, for example, http redirects are followed. + +When an `easy_perform()` starts, the transfer's `data->state.initial_origin` +peer is cleared. When creating the connection, `data->state.origin` is +calculated (e.g. who the request talks to). If `data->state.initial_origin` +is not set, the first `data->state.origin` is linked there. +Now `libcurl` knows where +the transfer initially talked to on all possible subsequent requests. + +Credential information from `CURLOPT_*` settings is only applicable for the +initial origin. Any followup request going to another origin must not +use it. Therefore `data->state.creds` is *only* created from `CURLOPT_*` +when current origin and initial origin match. + +Without credentials from `CURLOPT_*`, the URL is inspected for user and +password and `netrc` is consulted as well (when built in). + +### `conn->creds` + +Once `data->state.creds` is known, the connection credentials are +determined. For protocols that tie authorization to everything send +on a connection (protocols without flag `PROTOPT_CREDSPERREQUEST`), +`conn->creds` is linked to `data->state.creds`. Only connections +carrying the same credentials may be reused. + +Protocol with flag `PROTOPT_CREDSPERREQUEST` leave `conn->creds` empty, +as connections for such protocols may be reused with different +credentials. + +That being said, there are authentication schemes like `NTLM` and +`NEGOTIATE` that tie credentials to a connection. Those do set `conn->creds` +once they start to operate, preventing connection reuse from then on +for transfers with different credentials. + +### `conn->*_proxy.creds` + +Those are set during connection setup from the `CURLOPT_*` values. They +do not require any "initial origin" handling as the origin of a proxy +does not change for a transfer. diff --git a/docs/internals/DYNBUF.md b/docs/internals/DYNBUF.md index 1ae7131f977e..d28b02809dc9 100644 --- a/docs/internals/DYNBUF.md +++ b/docs/internals/DYNBUF.md @@ -9,7 +9,7 @@ SPDX-License-Identifier: curl This is the internal module for creating and handling "dynamic buffers". This means buffers that can be appended to, dynamically and grow to adapt. -There is always a terminating zero put at the end of the dynamic buffer. +There is always a null-terminator put at the end of the dynamic buffer. The `struct dynbuf` is used to hold data for each instance of a dynamic buffer. The members of that struct **MUST NOT** be accessed or modified @@ -120,8 +120,8 @@ trusted or used anymore after the next buffer manipulation call. size_t curlx_dyn_len(const struct dynbuf *s); ``` -Returns the length of the buffer in bytes. Does not include the terminating -zero byte. +Returns the length of the buffer in bytes. Does not include the +null-terminator byte. ## `curlx_dyn_setlen` diff --git a/docs/internals/PEERS.md b/docs/internals/PEERS.md new file mode 100644 index 000000000000..28d002a92685 --- /dev/null +++ b/docs/internals/PEERS.md @@ -0,0 +1,130 @@ + + +# curl peers + +A `peer` in curl internals is represented by a `struct Curl_peer`. It has the following members: + +* `scheme`: a `struct Curl_scheme` of the URL schemes known to curl +* `user_hostname`: the hostname as supplied by the user/application +* `hostname`: a *normalized* version of `user_hostname` +* `port`: the network port +* `ipv6`: if `hostname` is an IPv6 address +* `unix_socket`: if `hostname` is a path to a `unix domain socket` +* `user_ipv6zone`: user supplied IPv6 zone name or `NULL` +* `ipv6scope_id`: IPv6 address scope or 0 +* `abstract`: (if `unix_socket`) if the socket is abstract + +A peer, in short, is a communication endpoint. + +## peers and transfers + +The peer a transfer, e.g. easy handle, works against is determined at the +start of each request. It is kept in `data->state.origin`. For the first +request done in a `curl_easy_perform()` or equivalent, this origin is +linked to `data->state.initial_origin`. This allows checks if properties +of `data->set.*` should apply to a request or not. + +`data->state.origin` is relevant for cookie processing, signing requests +and other request/response based processing. + +## peers and connections + +A network connection always goes *somewhere*. That *somewhere* is called +the `origin` of the connection (e.g. the source of responses/downloads). +It is kept in `conn->origin` and is always present in a connection. + +The `origin` is *logical* endpoint a connection talks to. In most +configurations it is the same as `data->state.origin` (see proxies below). + +For most connections, the `origin` is connected to *directly*. It +can be directed to another peer, however. + +### `connect-to` + +With the command line option `--connect-to` or the `libcurl` option +`CURLOPT_CONNECT_TO`, a connection can be told to make the network connection +to another endpoint *while keeping the `origin` unchanged*. + +This other endpoint is also a peer and is available as `conn->via_peer`. +This may be a peer for a different hostname and port or it may be a +`unix domain socket`. + +### proxies + +When a connection uses a proxy, the endpoint for contacting the proxy server +is also represented as a peer and is kept at `conn->socks_proxy.peer` and/or +`conn->http_proxy.peer`. `SOCKS` proxies always come first, so a connection +might connect as: + +``` +1. curl -------------------------------------------> conn->origin +2. curl -------------------------------------------> conn->via_peer (acting as conn->origin) +3. curl --> socks_proxy.peer ----------------------> conn->via_peer/origin +4. curl -----------------------> http_proxy.peer --> conn->via_peer/origin +5. curl --> socks_proxy.peer --> http_proxy.peer --> conn->via_peer/origin +``` + +A `conn->(socks|http)_proxy.peer` is only ever present when the proxy +is in use and `NULL` otherwise. + +SOCKS proxies are always used for tunneling, either to the origin or +the HTTP proxy. They operate in a connection filter. + +HTTP proxies can operate in two modes: tunneling or forwarding. When tunneling, +they also operate in a connection filter. In forwarding mode however, they +become the `origin` the connection talks to. + +Therefore, connections that talk to a forwarding HTTP proxy have `conn->origin` +set to `conn->http_proxy.peer` and `conn->bits.origin_is_proxy` is set. + +The connection filter `SETUP`, that assembles the filters for a connection, +figures out which peer to pass to which filter in order to make it all work. +The individual filters get passed a specific peer and do not need be concerned +with the whole chain. + +For example, IP connection goes to `origin`(1), `via_peer`(2), +`socks_proxy.peer`(3+5), `http_proxy.peer`(4) and that is the peer that gets +passed to the `DNS` and `HAPPY-EYEBALLS` filters. + +### TLS + +TLS filters' task is to verify the peer they talk to (unless that is +switched off). They either talk to the `conn->origin` or the +`conn->http_proxy.peer` (`SOCKS` does not have TLS). The `conn->via_peer` is +irrelevant. A `via_peer` endpoint needs to present a certificate matching +`conn->origin` or the connect must fail. + +### `unix domain socket`s + +Peers that represent a `unix domain socket` may be used in two places: + +1. `via_peer`: curl can connect to an `origin` server via `unix domain socket`s. + This disables any proxy settings a transfer might carry. +2. `socks_proxy.peer`: a `SOCKS` proxy may be contacted over a `unix domain + socket`. + +It is not supported to contact an http proxy over `unix domain socket`s. + +## peers and credentials + +There have been several vulnerabilities by leaking credentials in requests +where they should not appear. In future work we plan to tie credentials to +`peers` and use them only when their `peer` still matches the current +connection use. + +## peers internals + +A `struct Curl_peer` is allocated with space of the `user_hostname`. +Only when the user supplied value needs conversions (removing `[]` or +IDN encoding) is `hostname` an extra allocation. This keeps the number +of allocations the same as before. + +A `Curl_peer` is not expected to be modified after it has been created. +However, each `Curl_peer` has a reference counter. Code needs to use +`Curl_peer_link()` and `Curl_peer_unlink()` to keep/release references. +This makes it safe and cheap to keep references to peers in connections +and filters. diff --git a/docs/internals/TLS-SESSIONS.md b/docs/internals/TLS-SESSIONS.md index b108fbfcfbe2..c3bf7038b447 100644 --- a/docs/internals/TLS-SESSIONS.md +++ b/docs/internals/TLS-SESSIONS.md @@ -119,7 +119,7 @@ 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 +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 is not only privacy relevant, but also has security implications as an attacker might find worthy targets among your peer keys. diff --git a/docs/libcurl/curl_easy_duphandle.md b/docs/libcurl/curl_easy_duphandle.md index 5ea9c4fba018..0de33496cacd 100644 --- a/docs/libcurl/curl_easy_duphandle.md +++ b/docs/libcurl/curl_easy_duphandle.md @@ -42,7 +42,9 @@ SSL sessions and no cookies. It also does not inherit any share object states or options (created as if CURLOPT_SHARE(3) was set to NULL). If the source handle has HSTS or alt-svc enabled, the duplicate gets data read -data from the main filename to populate the cache. +from the main filename to populate the cache. For HSTS, any entries learned at +runtime (E.g. `Strict-Transport-Security` response headers) are also copied to +the duplicate handle. In multi-threaded programs, this function must be called in a synchronous way, the input handle may not be in use when cloned. diff --git a/docs/libcurl/curl_easy_pause.md b/docs/libcurl/curl_easy_pause.md index 31e1cffe77c0..4a37ed1c14a7 100644 --- a/docs/libcurl/curl_easy_pause.md +++ b/docs/libcurl/curl_easy_pause.md @@ -80,19 +80,17 @@ Convenience define that unpauses both directions. # LIMITATIONS The pausing of transfers does not work with protocols that work without -network connectivity, like FILE://. Trying to pause such a transfer, in any +network connectivity, like `file://`. Trying to pause such a transfer, in any direction, might cause problems or error. # MULTIPLEXED -When a connection is used multiplexed, like for HTTP/2, and one of the -transfers over the connection is paused and the others continue flowing, -libcurl might end up buffering contents for the paused transfer. It has to do -this because it needs to drain the socket for the other transfers and the -already announced window size for the paused transfer allows the server to -continue sending data up to that window size amount. By default, libcurl -announces a 32 megabyte window size, which thus can make libcurl end up -buffering 32 megabyte of data for a paused stream. +On multiplexed connections (HTTP/2 or HTTP/3), pausing an individual stream +while others remain active forces libcurl to buffer up to 10 MB of data for +the paused transfer. Because libcurl must continuously drain the shared socket +to sustain active streams, and the default flow-control window allows the +server to send up to 10 MB before halting, libcurl is forced to buffer the +incoming bytes in memory. When such a paused stream is unpaused again, any buffered data is delivered first. diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index 13d966f3c319..5f75596cff60 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -50,6 +50,9 @@ any way reset between transfers, so if you want subsequent transfers with different options, you must change them between the transfers. You can optionally reset all options back to internal default with curl_easy_reset(3). +Changing options with curl_easy_setopt(3) while a transfer is still in +progress may cause undefined and undesired behavior. + The order in which the options are set does not matter. # STRINGS @@ -69,7 +72,7 @@ Passing in "creative octets" like newlines where they are not expected might trigger unexpected results. Before version 7.17.0, strings were not copied. Instead the user was forced -keep them available until libcurl no longer needed them. +to keep them available until libcurl no longer needed them. # OPTIONS @@ -1162,11 +1165,12 @@ Redirect stderr to another stream. See CURLOPT_STDERR(3) ## CURLOPT_STREAM_DEPENDS -This HTTP/2 stream depends on another. See CURLOPT_STREAM_DEPENDS(3) +**Deprecated option** This HTTP/2 stream depends on another. See +CURLOPT_STREAM_DEPENDS(3) ## CURLOPT_STREAM_DEPENDS_E -This HTTP/2 stream depends on another exclusively. See +**Deprecated option** This HTTP/2 stream depends on another exclusively. See CURLOPT_STREAM_DEPENDS_E(3) ## CURLOPT_STREAM_WEIGHT diff --git a/docs/libcurl/curl_easy_ssls_export.md b/docs/libcurl/curl_easy_ssls_export.md index fdefa408e524..1eb634fb5887 100644 --- a/docs/libcurl/curl_easy_ssls_export.md +++ b/docs/libcurl/curl_easy_ssls_export.md @@ -149,8 +149,7 @@ int main(void) if(curl) { curl_easy_setopt(curl, CURLOPT_SHARE, share); - /* run a transfer, all TLS sessions received will be added - * to the share. */ + /* run a transfer, all TLS sessions received are added to the share. */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_perform(curl); diff --git a/docs/libcurl/curl_formadd.md b/docs/libcurl/curl_formadd.md index 94e6c269bdcc..f988e038767f 100644 --- a/docs/libcurl/curl_formadd.md +++ b/docs/libcurl/curl_formadd.md @@ -112,6 +112,12 @@ 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. +## CURLFORM_NAMELENGTH + +followed by a long giving the length of the name. Pass this option to set +the length of *CURLFORM_COPYNAME* and *CURLFORM_PTRNAME* strings, if they are +not null-terminated. + ## CURLFORM_FILECONTENT followed by a filename, causes that file to be read and its contents used @@ -278,7 +284,7 @@ int main(void) CURLFORM_COPYNAME, "name", CURLFORM_BUFFER, "data", CURLFORM_BUFFERPTR, record, - CURLFORM_BUFFERLENGTH, sizeof(record), + CURLFORM_BUFFERLENGTH, (long)sizeof(record), CURLFORM_END); /* no option needed for the end marker */ diff --git a/docs/libcurl/curl_global_sslset.md b/docs/libcurl/curl_global_sslset.md index 8ef0ca99923b..ccc7b7aa616c 100644 --- a/docs/libcurl/curl_global_sslset.md +++ b/docs/libcurl/curl_global_sslset.md @@ -40,7 +40,7 @@ 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. The available backends are those +null-terminated list of available backends. The available backends are those that this particular build of libcurl supports. Since libcurl 7.60.0, the *avail* pointer is always set to the list of @@ -50,7 +50,7 @@ Upon success, the function returns **CURLSSLSET_OK**. If the specified SSL backend is not available, the function returns **CURLSSLSET_UNKNOWN_BACKEND** and sets the *avail* pointer to a -NULL-terminated list of available SSL backends. In this case, you may call the +null-terminated list of available SSL backends. In this case, you may call the function again to try to select a different backend. The SSL backend can be set only once. If it has already been set, a subsequent @@ -70,11 +70,11 @@ SSL backend names (case-insensitive): GnuTLS, mbedTLS, OpenSSL, Rustls, 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 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. +forks/flavors in this function. AmiSSL, AWS-LC, BoringSSL, LibreSSL, OpenSSL +and quictls are all supported by libcurl, but in the eyes of +curl_global_sslset(3) 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. # struct diff --git a/docs/libcurl/curl_mprintf.md b/docs/libcurl/curl_mprintf.md index 72ee0a1f0229..7c9def535c8a 100644 --- a/docs/libcurl/curl_mprintf.md +++ b/docs/libcurl/curl_mprintf.md @@ -54,7 +54,7 @@ write output to stdout, the standard output stream; **curl_mfprintf()** and **curl_mvsnprintf()** write to the character string **buffer**. The functions **curl_msnprintf()** and **curl_mvsnprintf()** write at most -*maxlength* bytes (including the terminating null byte ('0')) to +*maxlength* bytes (including the null-terminator byte ('0')) to *buffer*. The functions **curl_mvprintf()**, **curl_mvfprintf()**, @@ -246,10 +246,10 @@ is written. The *const char ** argument is expected to be a pointer to an array of character type (pointer to a string). Characters from the array are written up -to (but not including) a terminating null byte. If a precision is specified, +to (but not including) a null-terminator byte. If a precision is specified, no more than the number specified are written. If a precision is given, no null byte need be present; if the precision is not specified, or is greater -than the size of the array, the array must contain a terminating null byte. +than the size of the array, the array must contain a null-terminator byte. ## p diff --git a/docs/libcurl/curl_multi_assign.md b/docs/libcurl/curl_multi_assign.md index 279965f53436..784f5ad9a9cc 100644 --- a/docs/libcurl/curl_multi_assign.md +++ b/docs/libcurl/curl_multi_assign.md @@ -7,6 +7,7 @@ Source: libcurl See-also: - curl_multi_setopt (3) - curl_multi_socket_action (3) + - CURLMOPT_SOCKETFUNCTION (3) Protocol: - All Added-in: 7.15.5 @@ -31,11 +32,12 @@ This function creates an association in the multi handle between the given socket and a private pointer of the application. This is designed for curl_multi_socket_action(3) uses. -When set, the *sockptr* pointer is passed to all future socket callbacks -for the specific *sockfd* socket. +When set, the *sockptr* pointer is passed to all future socket callbacks for +the specific *sockfd* socket, until the socket stops being monitored +(CURL_POLL_REMOVE is sent to the CURLMOPT_SOCKETFUNCTION(3) callback). -If the given *sockfd* is not already in use by libcurl, this function -returns an error. +If the given *sockfd* is not already in use by libcurl, this function returns +an error. 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 diff --git a/docs/libcurl/curl_multi_socket_action.md b/docs/libcurl/curl_multi_socket_action.md index 44a06406c3e5..4d690fd97d26 100644 --- a/docs/libcurl/curl_multi_socket_action.md +++ b/docs/libcurl/curl_multi_socket_action.md @@ -12,7 +12,7 @@ See-also: - the hiperfifo.c example Protocol: - All -Added-in: 7.15.4 +Added-in: 7.16.3 --- # NAME @@ -92,7 +92,7 @@ to kickstart everything. To get one or more callbacks called. 7. Wait for activity on any of libcurl's sockets, use the timeout value your callback has been told. -8, When activity is detected, call curl_multi_socket_action() for the +8. When activity is detected, call curl_multi_socket_action() for the socket(s) that got action. If no activity is detected and the timeout expires, call curl_multi_socket_action(3) with *CURL_SOCKET_TIMEOUT*. @@ -103,7 +103,7 @@ call curl_multi_socket_action(3) with *CURL_SOCKET_TIMEOUT*. ~~~c int main(void) { - /* the event-library gets told when there activity on the socket 'fd', + /* the event-library gets told when there is activity on the socket 'fd', which we translate to a call to curl_multi_socket_action() */ int running = 0; int fd = 3; /* the descriptor that had action */ diff --git a/docs/libcurl/curl_version_info.md b/docs/libcurl/curl_version_info.md index fd589a834cc3..5b1822216995 100644 --- a/docs/libcurl/curl_version_info.md +++ b/docs/libcurl/curl_version_info.md @@ -298,6 +298,13 @@ supports HTTP NTLM libcurl was built with support for NTLM delegation to a winbind helper. This feature was removed from curl in 8.8.0. +## `proxy-HTTP3` + +*features* mask bit: non-existent + +libcurl was built with EXPERIMENTAL support for HTTP/3 proxy tunneling +(Added in 8.21.0) + ## `PSL` *features* mask bit: CURL_VERSION_PSL diff --git a/docs/libcurl/libcurl-easy.md b/docs/libcurl/libcurl-easy.md index 782108835678..fdbc75582371 100644 --- a/docs/libcurl/libcurl-easy.md +++ b/docs/libcurl/libcurl-easy.md @@ -38,14 +38,14 @@ you see what libcurl is doing under the hood, which is useful when debugging for example. The curl_easy_setopt(3) man page has a full index of the over 300 available options. -If you at any point would like to blank all previously set options for a -single easy handle, you can call curl_easy_reset(3) and you can also make a +If you at any point would like to factory-reset all previously set options for +a single easy handle, you can call curl_easy_reset(3). You can also make a clone of an easy handle (with all its set options) using curl_easy_duphandle(3). -When all is setup, you tell libcurl to perform the transfer using -curl_easy_perform(3). It performs the entire transfer operation and does not -return until it is done (successfully or not). +When all necessary options have been set on the handle, you tell libcurl to +perform the transfer with curl_easy_perform(3). It performs the entire +transfer operation and does not return until it is done (successfully or not). After the transfer has been made, you can set new options and make another transfer, or if you are done, cleanup the session by calling diff --git a/docs/libcurl/libcurl-env-dbg.md b/docs/libcurl/libcurl-env-dbg.md index 9fa9c069d1f9..70d0f12c88bd 100644 --- a/docs/libcurl/libcurl-env-dbg.md +++ b/docs/libcurl/libcurl-env-dbg.md @@ -199,3 +199,8 @@ Make `curl` use the quick exit option, even when built in debug mode. When happy eyeballing for https: wait for the HTTPS-RR resolve answer to arrive before starting any connect attempt. + +## `CURL_DBG_NO_USE_SSL_ON_FIRST` + +When passing `--ssl-reqd`, clear it for the first URL in a curl command. +This allows testing of connection reuse in mixed `STARTTLS` needs. diff --git a/docs/libcurl/libcurl-env.md b/docs/libcurl/libcurl-env.md index 6ef11ac9a3dd..5ce846df8aa6 100644 --- a/docs/libcurl/libcurl-env.md +++ b/docs/libcurl/libcurl-env.md @@ -32,8 +32,8 @@ uses the **ftp_proxy** variable. These proxy variables are also checked for in their uppercase versions, except the **http_proxy** one which is only used lowercase. Note also that some -systems actually have a case insensitive handling of environment variables and -then of course **HTTP_PROXY** still works. +systems (like Windows) have a case insensitive handling of environment +variables and then of course **HTTP_PROXY** still works. An exception exists for the WebSocket **ws** and **wss** URL schemes, where libcurl first checks **ws_proxy** or **wss_proxy** but if they are not set, it diff --git a/docs/libcurl/libcurl-errors.md b/docs/libcurl/libcurl-errors.md index 7ae1319dcf80..c658361a7920 100644 --- a/docs/libcurl/libcurl-errors.md +++ b/docs/libcurl/libcurl-errors.md @@ -219,7 +219,7 @@ file boundary. ## CURLE_FILE_COULDNT_READ_FILE (37) -A file given with FILE:// could not be opened. Most likely because the file +A file given with `file://` could not be opened. Most likely because the file path does not identify an existing file. Did you check file permissions? ## CURLE_LDAP_CANNOT_BIND (38) @@ -674,7 +674,7 @@ There is no zone id set in the URL. ## CURLUE_BAD_FILE_URL (19) -The file:// URL is invalid. +The `file://` URL is invalid. ## CURLUE_BAD_FRAGMENT (20) diff --git a/docs/libcurl/libcurl-multi.md b/docs/libcurl/libcurl-multi.md index b400f61a03cc..da1331fae82c 100644 --- a/docs/libcurl/libcurl-multi.md +++ b/docs/libcurl/libcurl-multi.md @@ -175,8 +175,6 @@ A few areas in the code are still using blocking code, even when used from the 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 +- `file://` transfers - TELNET transfers -~~~ diff --git a/docs/libcurl/libcurl-security.md b/docs/libcurl/libcurl-security.md index cca0c31e8420..81b29cfd16f0 100644 --- a/docs/libcurl/libcurl-security.md +++ b/docs/libcurl/libcurl-security.md @@ -267,16 +267,16 @@ of how the SCP protocol is designed. E.g. Applications must not allow unsanitized SCP: URLs to be passed in for downloads. -# file:// +# `file://` -By default curl and libcurl support file:// URLs. Such a URL is always an +By default curl and libcurl support `file://` URLs. Such a URL is always an access, or attempted access, to a local resource. If your application wants to avoid that, keep control of what URLs to use and/or prevent curl/libcurl from using the protocol. -By default, libcurl prohibits redirects to file:// URLs. +By default, libcurl prohibits redirects to `file://` URLs. -# Warning: file:// on Windows +# Warning: `file://` on Windows The Windows operating system tries automatically, and without any way for applications to disable it, to establish a connection to another host over the diff --git a/docs/libcurl/libcurl-thread.md b/docs/libcurl/libcurl-thread.md index 8ef893f07588..9b26224ce6e6 100644 --- a/docs/libcurl/libcurl-thread.md +++ b/docs/libcurl/libcurl-thread.md @@ -13,12 +13,12 @@ Added-in: n/a # NAME -libcurl-thread - libcurl thread safety +libcurl-thread - libcurl thread-safety # Multi-threading with libcurl 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 +to provide your own locking should you meet any of the thread-safety exceptions below. # Handles diff --git a/docs/libcurl/libcurl-tutorial.md b/docs/libcurl/libcurl-tutorial.md index 704ee0356416..befcf7739968 100644 --- a/docs/libcurl/libcurl-tutorial.md +++ b/docs/libcurl/libcurl-tutorial.md @@ -373,7 +373,7 @@ them URL encoded, as %XX where XX is a two-digit hexadecimal number. libcurl also provides options to set various passwords. The username and password as shown embedded in the URL can instead get set with the 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: +`char *` to a string in the format `"user:password"`. In a manner like this: ~~~c curl_easy_setopt(handle, CURLOPT_USERPWD, "myname:thesecret"); @@ -391,7 +391,7 @@ CURLOPT_USERPWD(3) option, like this: There is a long time Unix "standard" way of storing FTP usernames and passwords, namely in the $HOME/.netrc file (on Windows, libcurl also checks the *%USERPROFILE% environment* variable if *%HOME%* is unset, and tries -"_netrc" as name). The file should be made private so that only the user may +`_netrc` as name). The file should be made private so that only the user may read it (see also the "Security Considerations" chapter), as it might contain the password in plain text. libcurl has the ability to use this file to figure out what set of username and password to use for a particular host. As an @@ -866,23 +866,23 @@ it defaults to assuming an HTTP proxy): libcurl automatically checks and uses a set of environment variables to know what proxies to use for certain protocols. The names of the variables are -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 +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 +named `ftp_proxy` is checked for FTP URLs. Again, the proxies are always 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 -"[protocol://][user:password@]machine[:port]". Where the protocol:// part +`[protocol://][user:password@]machine[:port]`. Where the `protocol://` part specifies which type of proxy it is, and the optional port number specifies on which port the proxy operates. If not specified, the internal default port number is used and that is most likely not the one you would like it to be. -There are two special environment variables. 'all_proxy' is what sets proxy -for any URL in case the protocol specific variable was not set, and 'no_proxy' +There are two special environment variables. `all_proxy` is what sets proxy +for any URL in case the protocol specific variable was not set, and `no_proxy` defines a list of hosts that should not use a proxy even though a variable may -say so. If 'no_proxy' is a plain asterisk ("*") it matches all hosts. +say so. If `no_proxy` is a plain asterisk (`*`) it matches all hosts. To explicitly disable libcurl's checking for and using the proxy environment variables, set the proxy name to "" - an empty string - with @@ -1420,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 diff --git a/docs/libcurl/libcurl-ws.md b/docs/libcurl/libcurl-ws.md index 147740157fee..cce13abb243f 100644 --- a/docs/libcurl/libcurl-ws.md +++ b/docs/libcurl/libcurl-ws.md @@ -39,7 +39,7 @@ WebSocket is a TCP-like message-based communication protocol done over HTTP, specified in RFC 6455. To initiate a WebSocket session with libcurl, setup an easy handle to use a -URL with a "WS://" or "WSS://" scheme. "WS" is for cleartext communication +URL with a `ws://` or `wss://` scheme. "WS" is for cleartext communication over HTTP and "WSS" is for doing WebSocket securely over HTTPS. A WebSocket request is done as an HTTP/1 GET request with an "Upgrade diff --git a/docs/libcurl/libcurl.md b/docs/libcurl/libcurl.md index 96f10656eed9..da61d4c5ea51 100644 --- a/docs/libcurl/libcurl.md +++ b/docs/libcurl/libcurl.md @@ -174,7 +174,7 @@ to select the active SSL backend. 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. +(most platforms). Read libcurl-thread(3) for thread-safety guidelines. 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 diff --git a/docs/libcurl/mksymbolsmanpage.pl b/docs/libcurl/mksymbolsmanpage.pl index c70e99495758..9db028e7c4fc 100755 --- a/docs/libcurl/mksymbolsmanpage.pl +++ b/docs/libcurl/mksymbolsmanpage.pl @@ -70,7 +70,7 @@ ; sub nameref { - my ($n)=@_; + my ($n) = @_; if($n =~ /^CURLOPT_/) { if($n eq "CURLOPT_RTSPHEADER") { $n = "CURLOPT_HTTPHEADER"; @@ -289,7 +289,7 @@ sub nameref { while() { if($_ =~ /^(CURL[A-Z0-9_.]*) *(.*)/i) { - my ($symbol, $rest)=($1,$2); + my ($symbol, $rest) = ($1, $2); my ($intro, $dep, $rem); if($rest =~ s/^([0-9.]*) *//) { $intro = $1; diff --git a/docs/libcurl/opts/CURLINFO_CAINFO.md b/docs/libcurl/opts/CURLINFO_CAINFO.md index 23502d22966a..626abca19e3f 100644 --- a/docs/libcurl/opts/CURLINFO_CAINFO.md +++ b/docs/libcurl/opts/CURLINFO_CAINFO.md @@ -53,7 +53,7 @@ int main(void) char *cainfo = NULL; curl_easy_getinfo(curl, CURLINFO_CAINFO, &cainfo); if(cainfo) { - printf("default ca info path: %s\n", cainfo); + printf("default CA info path: %s\n", cainfo); } curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLINFO_CAPATH.md b/docs/libcurl/opts/CURLINFO_CAPATH.md index c58930e0e9bc..603634da8a4d 100644 --- a/docs/libcurl/opts/CURLINFO_CAPATH.md +++ b/docs/libcurl/opts/CURLINFO_CAPATH.md @@ -56,7 +56,7 @@ int main(void) char *capath = NULL; curl_easy_getinfo(curl, CURLINFO_CAPATH, &capath); if(capath) { - printf("default ca path: %s\n", capath); + printf("default CA path: %s\n", capath); } curl_easy_cleanup(curl); } diff --git a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md index 219481f43df1..7b5d05852155 100644 --- a/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md +++ b/docs/libcurl/opts/CURLINFO_CONTENT_LENGTH_UPLOAD_T.md @@ -31,6 +31,11 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_CONTENT_LENGTH_UPLOAD_T, Pass a pointer to a *curl_off_t* to receive the specified size of the upload. Stores -1 if the size is not known. +This is the size set by the client prior to the transfer start. The expected +upload amount. Compare this with CURLINFO_SIZE_UPLOAD_T(3), which is the +amount of data that was actually uploaded in the end. In many cases those two +numbers are identical. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md b/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md index c27856809ee5..f0ce241e3f00 100644 --- a/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md +++ b/docs/libcurl/opts/CURLINFO_HEADER_SIZE.md @@ -29,11 +29,15 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_HEADER_SIZE, long *sizep); # DESCRIPTION Pass a pointer to a long to receive the total size of all the headers -received. Measured in number of bytes. +received, represented in HTTP/1-style header format. Measured in number of +bytes. The total includes the size of any received headers suppressed by CURLOPT_SUPPRESS_CONNECT_HEADERS(3). +The number of bytes transferred over the wire (or to the TLS backend) is +different when using HTTP/2 or greater. + # %PROTOCOLS% # EXAMPLE diff --git a/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md b/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md index 411b856e9328..2774ccbfc7f4 100644 --- a/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md +++ b/docs/libcurl/opts/CURLMOPT_SOCKETFUNCTION.md @@ -22,13 +22,14 @@ CURLMOPT_SOCKETFUNCTION - callback informed about what to wait for ~~~c #include -int socket_callback(CURL *easy, /* easy handle */ - curl_socket_t s, /* socket */ - int what, /* describes the socket */ - void *clientp, /* private callback pointer */ - void *socketp); /* private socket pointer */ - -CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_SOCKETFUNCTION, socket_callback); +int socket_callback(CURL *easy, + curl_socket_t socket, + int what, + void *clientp, + void *socketp); + +CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_SOCKETFUNCTION, + socket_callback); ~~~ # DESCRIPTION @@ -36,12 +37,12 @@ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_SOCKETFUNCTION, socket_callb Pass a pointer to your callback function, which should match the prototype shown above. -When the curl_multi_socket_action(3) function is called, it uses this -callback to inform the application about updates in the socket (file -descriptor) status by doing none, one, or multiple calls to the -**socket_callback**. The callback function gets status updates with changes -since the previous time the callback was called. If the given callback pointer -is set to NULL, no callback is called. +When the curl_multi_socket_action(3) function is called, it uses this callback +to inform the application about updates in the socket (file descriptor) status +by doing none, one, or multiple calls to the **socket_callback**. The callback +function gets status updates with changes since the previous time the callback +was called. If the given callback pointer is set to NULL, no callback is +called. libcurl then expects the application to monitor the sockets for the specific activities and tell libcurl again when something happens on one of them. Tell @@ -52,18 +53,23 @@ This may even happen after all transfers are done and is *likely* to happen *during* a call to curl_multi_cleanup(3) when cached connections are shut down. +libcurl may use a number of internal file descriptors for name resolving, +Happy Eyeballs racing, internal communication and more, in addition to the +main sockets used for network transfers. All of those file descriptors might +get passed to this callback as "sockets". + # CALLBACK ARGUMENTS -*easy* identifies the specific transfer for which this update is related. +**easy** identifies the specific transfer for which this update is related. Since this callback manages a whole multi handle, an application should not make assumptions about which particular handle that is passed here. It might even be an internal easy handle that the application did not add itself. -*s* is the specific socket this function invocation concerns. If the -**what** argument is not CURL_POLL_REMOVE then it holds information about -what activity on this socket the application is supposed to -monitor. Subsequent calls to this callback might update the **what** bits -for a socket that is already monitored. +**socket** is the specific socket this function invocation concerns. If the +**what** argument is not CURL_POLL_REMOVE then it holds information about what +activity on this socket the application is supposed to monitor. Subsequent +calls to this callback might update the **what** bits for a socket that is +already monitored. The socket callback should return 0 on success, and -1 on error. If this callback returns error, **all** transfers currently in progress in this @@ -73,8 +79,8 @@ multi handle are aborted and made to fail. **socketp** is set with curl_multi_assign(3) or NULL. -The **what** parameter informs the callback on the status of the given -socket. It can hold one of these values: +The **what** parameter informs the callback on the status of the given socket. +It can hold one of these values: ## CURL_POLL_IN @@ -94,6 +100,14 @@ writable. The specified socket/file descriptor is no longer used by libcurl for any active transfer. It might soon be added again. +When a socket is given a CURL_POLL_REMOVE value, it might be because libcurl +is going to close it, but it might also mean that it does not need any more +monitoring for the moment. An application cannot assume either. The same +socket might appear soon in a call asking for monitoring again. + +A socket that is removed like this loses its assigned pointer as set with +curl_multi_assign(3). + # DEFAULT NULL (no callback) diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md index 838b2e5a5f07..c04ebe305156 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md @@ -37,7 +37,8 @@ struct curl_fileinfo { char *perm; char *user; char *group; - char *target; /* pointer to the target filename of a symlink */ + char *target; /* pointer to the target filename of a symlink, only + available for CURLFILETYPE_SYMLINK */ } strings; unsigned int flags; diff --git a/docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md b/docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md index 7435f9866840..e896b8b685ba 100644 --- a/docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md +++ b/docs/libcurl/opts/CURLOPT_DISALLOW_USERNAME_IN_URL.md @@ -29,12 +29,14 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_DISALLOW_USERNAME_IN_URL, # DESCRIPTION -A long parameter set to 1 tells the library to not allow URLs that include a -username. +A long parameter set to 1 tells the library to not allow URLs set with +CURLOPT_URL(3) that include a username. This is the equivalent to the *CURLU_DISALLOW_USER* flag for the curl_url_set(3) function. +Note that this option does not affect URLs set with CURLOPT_CURLU(3). + # DEFAULT 0 (disabled) diff --git a/docs/libcurl/opts/CURLOPT_DOH_URL.md b/docs/libcurl/opts/CURLOPT_DOH_URL.md index 1c463f13caff..696903c6baeb 100644 --- a/docs/libcurl/opts/CURLOPT_DOH_URL.md +++ b/docs/libcurl/opts/CURLOPT_DOH_URL.md @@ -47,6 +47,8 @@ option. Using this option multiple times makes the last set string override the previous ones. Set it to NULL to disable its use again. +DoH lookups do not inherit proxy options from its parent transfer. + # INHERIT OPTIONS DoH lookups use SSL and some SSL settings from your transfer are inherited, @@ -97,6 +99,6 @@ 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). -Note that curl_easy_setopt(3) does immediately parse the given string so when -given a bad DoH URL, libcurl might not detect the problem until it later tries -to resolve a name with it. +Note that curl_easy_setopt(3) does not immediately parse the given string so +when given a bad DoH URL, libcurl might not detect the problem until it later +tries to resolve a name with it. diff --git a/docs/libcurl/opts/CURLOPT_ECH.md b/docs/libcurl/opts/CURLOPT_ECH.md index e409d58cece4..99d3ddaefc33 100644 --- a/docs/libcurl/opts/CURLOPT_ECH.md +++ b/docs/libcurl/opts/CURLOPT_ECH.md @@ -29,14 +29,10 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ECH, char *config); # DESCRIPTION -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. +This feature is **experimental** and may change before it is considered +stable. We advise against using it in production. -There is also a known issue with using wolfSSL which does not support ECH when -the HelloRetryRequest mechanism is used. +ECH is only compatible with TLSv1.3. Pass a string that specifies configuration details for ECH. In all cases, if ECH is attempted, it may fail for various reasons. The keywords supported are: diff --git a/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md index 77ef0e06f33f..1cfb53de6012 100644 --- a/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md +++ b/docs/libcurl/opts/CURLOPT_HAPROXYPROTOCOL.md @@ -33,6 +33,10 @@ send this header. This option is primarily useful when sending test requests to a service that expects this header. +Note that the HAProxy protocol message is only is sent over a freshly setup +connection. A subsequent transfer that reuses a previous connection does not +send it again. + Most applications do not need this option. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_HSTS.md b/docs/libcurl/opts/CURLOPT_HSTS.md index 2dc0c457d6cd..12efff50bc79 100644 --- a/docs/libcurl/opts/CURLOPT_HSTS.md +++ b/docs/libcurl/opts/CURLOPT_HSTS.md @@ -66,7 +66,7 @@ NULL, no filename # SECURITY CONCERNS -We strongly urge users to stick to `HTTPS://` URLs, which makes this option +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 diff --git a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md index e05c84183a76..692178c72e53 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPAUTH.md +++ b/docs/libcurl/opts/CURLOPT_HTTPAUTH.md @@ -54,11 +54,8 @@ regular old-fashioned Basic method. ## CURLAUTH_DIGEST_IE -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 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. +The IE-specific Digest authentication behavior is no longer supported. +This bit is kept for compatibility and is treated as CURLAUTH_DIGEST. ## CURLAUTH_BEARER @@ -159,6 +156,8 @@ CURLAUTH_BEARER was added in 7.61.0 CURLAUTH_AWS_SIGV4 was added in 7.74.0 +CURLAUTH_DIGEST_IE does nothing since 8.21.0 + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_INTERLEAVEFUNCTION.md b/docs/libcurl/opts/CURLOPT_INTERLEAVEFUNCTION.md index 884c23fc898d..8cfe40406d01 100644 --- a/docs/libcurl/opts/CURLOPT_INTERLEAVEFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_INTERLEAVEFUNCTION.md @@ -38,7 +38,7 @@ interleaved RTP data. This function gets called for each $ block and therefore contains exactly one upper-layer protocol unit (e.g. one RTP packet). curl writes the interleaved header as well as the included data for each call. The first byte is always an ASCII dollar sign. The dollar sign is followed by a -one byte channel identifier and then a 2 byte integer length in network byte +one-byte channel identifier and then a 2-byte integer length in network byte order. See RFC 2326 Section 10.12 for more information on how RTP interleaving behaves. If unset or set to NULL, curl uses the default write function. diff --git a/docs/libcurl/opts/CURLOPT_KEYPASSWD.md b/docs/libcurl/opts/CURLOPT_KEYPASSWD.md index d97a94716436..38f5120cae8c 100644 --- a/docs/libcurl/opts/CURLOPT_KEYPASSWD.md +++ b/docs/libcurl/opts/CURLOPT_KEYPASSWD.md @@ -9,6 +9,8 @@ See-also: - CURLOPT_SSLKEY (3) Protocol: - TLS + - SFTP + - SCP TLS-backend: - OpenSSL - mbedTLS diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md index 17c4c9f4e176..a1a0d8496ccd 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md @@ -32,17 +32,16 @@ value, the transfer is aborted and *CURLE_FILESIZE_EXCEEDED* is returned. Passing a zero *size* disables this, and passing a negative *size* yields a *CURLE_BAD_FUNCTION_ARGUMENT*. -The file size is not always known prior to the download start, and for such -transfers this option has no effect - even if the file transfer eventually -ends up being larger than this given limit. - 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. +If the size is known to be too big before the transfer starts, libcurl +aborts before starting the transfer. If it is instead found to be too big +while the transfer is in progress, libcurl aborts the transfer once the +received bytes exceed the limit. -Since 8.20.0, this option also stops ongoing transfers that would reach this -threshold due to automatic decompression using CURLOPT_ACCEPT_ENCODING(3). +Since 8.20.0, this option also aborts ongoing transfers once the +decompressed bytes exceed this threshold due to automatic decompression using +CURLOPT_ACCEPT_ENCODING(3). # DEFAULT @@ -68,6 +67,10 @@ int main(void) # %AVAILABILITY% +# HISTORY + +Before curl 8.4.0, the limit was not applied to transfers in progress. + # RETURN VALUE curl_easy_setopt(3) returns a CURLcode indicating success or error. diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md b/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md index 791b25862f7e..929bae67f2c1 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE_LARGE.md @@ -29,18 +29,15 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MAXFILESIZE_LARGE, # DESCRIPTION -Pass a curl_off_t as parameter. This specifies the maximum accepted *size* -(in bytes) of a file to download. If the file requested is found larger than -this value, the transfer is aborted and *CURLE_FILESIZE_EXCEEDED* is -returned. Passing a zero *size* disables this, and passing a negative *size* -yields a *CURLE_BAD_FUNCTION_ARGUMENT*. +Pass a curl_off_t as parameter. This specifies the maximum accepted *size* (in +bytes) of a file to download. If the file requested is found larger than this +value, the transfer is aborted and *CURLE_FILESIZE_EXCEEDED* is returned. +Passing a zero *size* disables this, and passing a negative *size* yields a +*CURLE_BAD_FUNCTION_ARGUMENT*. -The file size is not always known prior to the download start, and for such -transfers this option has no effect - even if the file transfer eventually -ends up being larger than this given limit. - -Since 8.4.0, this option also stops ongoing transfers if they reach this -threshold. +If the size is known to exceed the limit before the transfer starts, libcurl +aborts before starting the transfer. If the transfer instead exceeds the limit +while it is in progress, libcurl aborts it at that point. Since 8.20.0, this option also stops ongoing transfers that would reach this threshold due to automatic decompression using CURLOPT_ACCEPT_ENCODING(3). @@ -70,6 +67,10 @@ int main(void) # %AVAILABILITY% +# HISTORY + +Before curl 8.4.0, the limit was not applied to transfers in progress. + # RETURN VALUE curl_easy_setopt(3) returns a CURLcode indicating success or error. diff --git a/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md b/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md index 9a9117669d62..ee0c7e84bfc4 100644 --- a/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAX_RECV_SPEED_LARGE.md @@ -38,7 +38,7 @@ the given threshold over a period time. If you set *maxspeed* to a value lower than CURLOPT_BUFFERSIZE(3), libcurl might download faster than the set limit initially. -This option does not affect transfer speeds done with FILE:// URLs. +This option does not affect transfer speeds done with `file://` URLs. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md index 34566ece0ded..d64d324afd78 100644 --- a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md @@ -39,7 +39,7 @@ If you set *maxspeed* to a value lower than CURLOPT_UPLOAD_BUFFERSIZE(3), libcurl might "shoot over" the limit on its first send and still send off a full buffer. -This option does not affect transfer speeds done with FILE:// URLs. +This option does not affect transfer speeds done with `file://` URLs. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md b/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md index cb6731912c72..264ae897cd69 100644 --- a/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md +++ b/docs/libcurl/opts/CURLOPT_NEW_DIRECTORY_PERMS.md @@ -33,7 +33,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_NEW_DIRECTORY_PERMS, Pass a long as a parameter, containing the value of the permissions that is set on newly created directories on the remote server. The default value is *0755*, but any valid value can be used. The only protocols that can use -this are *sftp://*, *scp://*, and *file://*. +this are `sftp://`, `scp://`, and `file://`. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md b/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md index 4c9579c8264e..00766f1e7e44 100644 --- a/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md +++ b/docs/libcurl/opts/CURLOPT_NEW_FILE_PERMS.md @@ -31,7 +31,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_NEW_FILE_PERMS, Pass a long as a parameter, containing the value of the permissions that are set on newly created files on the remote server. The default value is *0644*. -The only protocols that can use this are *sftp://*, *scp://*, and *file://*. +The only protocols that can use this are `sftp://`, `scp://`, and `file://`. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md index 0590143c906b..ed4011d57fa3 100644 --- a/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md +++ b/docs/libcurl/opts/CURLOPT_PINNEDPUBLICKEY.md @@ -36,9 +36,9 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PINNEDPUBLICKEY, # DESCRIPTION Pass a pointer to a null-terminated string as parameter. The string can be the -filename of your pinned public key. The file format expected is "PEM" or -"DER". The string can also be any number of base64 encoded sha256 hashes -preceded by "sha256//" and separated by ";" +filename of your pinned public key. The file format expected is `PEM` or +`DER`. The string can also be any number of base64 encoded sha256 hashes +preceded by `sha256//` and separated by `;`. When negotiating a TLS or SSL connection, the server sends a certificate indicating its identity. A public key is extracted from this certificate and @@ -53,6 +53,15 @@ On mismatch, *CURLE_SSL_PINNEDPUBKEYNOTMATCH* is returned. The application does not have to keep the string around after setting this option. +The pinned public key is used to verify the initial origin used in a transfer. +If the transfer is set to follow redirects to other origins, they are *not* +checked against this key. + +This option has no effect on LDAP connections when libcurl uses the legacy LDAP +backend. That backend manages TLS independently of curl's TLS layer. When +libcurl is built with USE_OPENLDAP, the OpenLDAP backend routes TLS through +curl's layer and this option is honored. + # DEFAULT NULL diff --git a/docs/libcurl/opts/CURLOPT_PORT.md b/docs/libcurl/opts/CURLOPT_PORT.md index 334f01b3d719..567d6a975a79 100644 --- a/docs/libcurl/opts/CURLOPT_PORT.md +++ b/docs/libcurl/opts/CURLOPT_PORT.md @@ -27,8 +27,8 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PORT, long number); # DESCRIPTION -We discourage using this option since its scope is not obvious and hard to -predict. Set the preferred port number in the URL instead. +We strongly discourage using this unreliable option since its scope is not +obvious and hard to predict. Set the preferred port number in the URL instead. 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 diff --git a/docs/libcurl/opts/CURLOPT_PRE_PROXY.md b/docs/libcurl/opts/CURLOPT_PRE_PROXY.md index 3b447fa907a7..06d0f320f48d 100644 --- a/docs/libcurl/opts/CURLOPT_PRE_PROXY.md +++ b/docs/libcurl/opts/CURLOPT_PRE_PROXY.md @@ -39,11 +39,11 @@ A pre proxy is a SOCKS proxy that curl connects to before it connects to the HTTP(S) proxy specified in the CURLOPT_PROXY(3) option. The pre proxy can only be a SOCKS proxy. -The pre proxy string should be prefixed with [scheme]:// to specify which kind -of socks is used. Use socks4://, socks4a://, socks5:// or socks5h:// (the last -one to enable socks5 and asking the proxy to do the resolving, also known as -*CURLPROXY_SOCKS5_HOSTNAME* type) to request the specific SOCKS version to -be used. Otherwise SOCKS4 is used as default. +The pre proxy string should be prefixed with `[scheme]://` to specify which +kind of socks is used. Use `socks4://`, `socks4a://`, `socks5://` or +`socks5h://` (the last one to enable socks5 and asking the proxy to do the +resolving, also known as *CURLPROXY_SOCKS5_HOSTNAME* type) to request the +specific SOCKS version to be used. Otherwise SOCKS4 is used as default. Setting the pre proxy string to "" (an empty string) explicitly disables the use of a pre proxy. diff --git a/docs/libcurl/opts/CURLOPT_PROXY.md b/docs/libcurl/opts/CURLOPT_PROXY.md index 3f8cf6bf5ddd..072dfd48093e 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY.md +++ b/docs/libcurl/opts/CURLOPT_PROXY.md @@ -33,12 +33,12 @@ should be a char * to a null-terminated string holding the hostname or dotted numerical IP address. A numerical IPv6 address must be written within [brackets]. -To specify port number in this string, append :[port] to the end of the host +To specify port number in this string, append `:[port]` to the end of the host name. The proxy's port number may optionally (but discouraged) be specified with the separate option CURLOPT_PROXYPORT(3). If not specified, libcurl defaults to using port 1080 for proxies. -The proxy string may be prefixed with [scheme]:// to specify which kind of +The proxy string may be prefixed with `[scheme]://` to specify which kind of proxy is used. Using this option multiple times makes the last set string override the @@ -47,30 +47,34 @@ 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. -## http:// +## `http://` HTTP Proxy. Default when no scheme or proxy type is specified. -## https:// +## `https://` 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. +Setting CURLOPT_PROXYTYPE(3) to **CURLPROXY_HTTPS3** allows libcurl to +negotiate using HTTP/3 with proxy. This feature is experimental and requires +a build with HTTP/3 proxy support enabled. + ## socks4:// SOCKS4 Proxy. -## socks4a:// +## `socks4a://` SOCKS4a Proxy. Proxy resolves URL hostname. -## socks5:// +## `socks5://` SOCKS5 Proxy. -## socks5h:// +## `socks5h://` SOCKS5 Proxy. Proxy resolves URL hostname. @@ -114,7 +118,7 @@ CURLOPT_PROXYPASSWORD(3). libcurl respects the proxy environment variables named **http_proxy**, **ftp_proxy**, **sftp_proxy** etc. If set, libcurl uses the specified proxy -for that URL scheme. For an "FTP://" URL, the **ftp_proxy** is +for that URL scheme. For an `ftp://` URL, the **ftp_proxy** is considered. **all_proxy** is used if no protocol specific proxy was set. If **no_proxy** (or **NO_PROXY**) is set, it is the exact equivalent of diff --git a/docs/libcurl/opts/CURLOPT_PROXYTYPE.md b/docs/libcurl/opts/CURLOPT_PROXYTYPE.md index 6000d10b00bc..1dc1a1328a01 100644 --- a/docs/libcurl/opts/CURLOPT_PROXYTYPE.md +++ b/docs/libcurl/opts/CURLOPT_PROXYTYPE.md @@ -41,6 +41,12 @@ HTTPS Proxy using HTTP/1. (Added in 7.52.0 for OpenSSL and GnuTLS. Since HTTPS Proxy and attempt to speak HTTP/2 over it. (Added in 8.1.0) +## CURLPROXY_HTTPS3 + +HTTPS Proxy and attempt to speak HTTP/3 over it. (Added in 8.21.0) +This feature is experimental and requires a build with HTTP/3 proxy support +enabled. + ## CURLPROXY_HTTP_1_0 HTTP 1.0 Proxy. This is similar to CURLPROXY_HTTP except it uses HTTP/1.0 for diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md index ab0e366b0d8c..0b491eeff653 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSL_OPTIONS.md @@ -38,10 +38,10 @@ behaviors. Available bits: 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 +the SSL layer libcurl uses may use a workaround 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 +**WARNING:** avoiding this workaround 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. @@ -58,7 +58,7 @@ Tells libcurl to not accept "partial" certificate chains, which it otherwise does by default. This option fails the certificate verification if the chain ends with an intermediate certificate and not with a root cert. -Works with OpenSSL and its forks (LibreSSL, BoringSSL, etc). (Added in 7.68.0) +Works with OpenSSL and its forks (BoringSSL, LibreSSL, etc). (Added in 7.68.0) Works with Schannel if the user specified certificates to verify the peer. (Added in 8.15.0) @@ -78,9 +78,9 @@ verification. 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. -Works with wolfSSL on Windows, Linux (Debian, Ubuntu, Gentoo, Fedora, RHEL), +Works with wolfSSL on Windows, Linux (Debian, Fedora, Gentoo, RHEL, Ubuntu), 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). +OpenSSL and its forks (BoringSSL, LibreSSL, etc) on Windows (Added in 7.71.0). ## CURLSSLOPT_AUTO_CLIENT_CERT diff --git a/docs/libcurl/opts/CURLOPT_SHARE.md b/docs/libcurl/opts/CURLOPT_SHARE.md index 2c6a496593fd..907e4f280519 100644 --- a/docs/libcurl/opts/CURLOPT_SHARE.md +++ b/docs/libcurl/opts/CURLOPT_SHARE.md @@ -43,6 +43,9 @@ if no share was used. Set this option to NULL again to stop using that share object. +Warning: adding a *share* and then setting it to NULL while the transfer +is ongoing is discouraged and may lead to undefined behavior. + # DEFAULT NULL diff --git a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md index 09aa83c875f6..1125ec392e25 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSH_HOSTKEYFUNCTION.md @@ -38,12 +38,15 @@ shown above. It overrides CURLOPT_SSH_KNOWNHOSTS(3). This callback gets called when the verification of the SSH host key is needed. -**key** is **keylen** bytes long and is the key to check. **keytype** -says what type it is, from the **CURLKHTYPE_*** series in the -**curl_khtype** enum. +**key** is **keylen** bytes long and is the key to check. **keytype** says +what type it is, from the **CURLKHTYPE_*** series in the **curl_khtype** enum. **clientp** is a custom pointer set with CURLOPT_SSH_HOSTKEYDATA(3). +This option is used to verify new SSH connections only. Once the connection +has been vetted by this callback it is deemed vetted and may be reused again +without invoking this callback again. + The callback must return one of the following return codes to tell libcurl how to act: 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 43a6d9e708e5..fce7e58f04bf 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md +++ b/docs/libcurl/opts/CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256.md @@ -73,10 +73,6 @@ int main(void) } ~~~ -# NOTES - -Requires the libssh2 backend. - # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_SSLVERSION.md b/docs/libcurl/opts/CURLOPT_SSLVERSION.md index 411da989efbf..cbe7a89c30c9 100644 --- a/docs/libcurl/opts/CURLOPT_SSLVERSION.md +++ b/docs/libcurl/opts/CURLOPT_SSLVERSION.md @@ -34,43 +34,43 @@ Pass a long as parameter to control which version range of SSL/TLS versions to use. The SSL and TLS versions have typically developed from the most insecure -version to be more and more secure in this order through history: SSL v2, -SSLv3, TLS v1.0, TLS v1.1, TLS v1.2 and the most recent TLS v1.3. +version to be more and more secure in this order through history: SSLv2, +SSLv3, TLSv1.0, TLSv1.1, TLSv1.2 and the most recent TLSv1.3. Use one of the available defines for this purpose. The available options are: ## CURL_SSLVERSION_DEFAULT The default acceptable version range. The minimum acceptable version is by -default TLS v1.2 since 8.16.0 (unless the TLS library has a stricter rule). +default TLSv1.2 since 8.16.0 (unless the TLS library has a stricter rule). ## CURL_SSLVERSION_TLSv1 -TLS v1.0 or later +TLSv1.0 or later ## CURL_SSLVERSION_SSLv2 -SSL v2 - refused +SSLv2 - refused ## CURL_SSLVERSION_SSLv3 -SSL v3 - refused +SSLv3 - refused ## CURL_SSLVERSION_TLSv1_0 -TLS v1.0 or later +TLSv1.0 or later ## CURL_SSLVERSION_TLSv1_1 -TLS v1.1 or later +TLSv1.1 or later ## CURL_SSLVERSION_TLSv1_2 -TLS v1.2 or later +TLSv1.2 or later ## CURL_SSLVERSION_TLSv1_3 -TLS v1.3 or later +TLSv1.3 or later ## @@ -82,24 +82,24 @@ with *one* of the CURL_SSLVERSION_MAX_ macros. 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 +which was TLSv1.2 up to before 7.61.0 and is TLSv1.3 since then - assuming the TLS library support it. ## CURL_SSLVERSION_MAX_TLSv1_0 -The flag defines maximum supported TLS version as TLS v1.0. +The flag defines maximum supported TLS version as TLSv1.0. ## CURL_SSLVERSION_MAX_TLSv1_1 -The flag defines maximum supported TLS version as TLS v1.1. +The flag defines maximum supported TLS version as TLSv1.1. ## CURL_SSLVERSION_MAX_TLSv1_2 -The flag defines maximum supported TLS version as TLS v1.2. +The flag defines maximum supported TLS version as TLSv1.2. ## CURL_SSLVERSION_MAX_TLSv1_3 -The flag defines maximum supported TLS version as TLS v1.3. +The flag defines maximum supported TLS version as TLSv1.3. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md index a2132ee5b367..73a16ae41204 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md +++ b/docs/libcurl/opts/CURLOPT_SSL_CTX_DATA.md @@ -30,7 +30,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSL_CTX_DATA, void *pointer); # DESCRIPTION -Data *pointer* to pass to the ssl context callback set by the option +Data *pointer* to pass to the SSL context callback set by the option CURLOPT_SSL_CTX_FUNCTION(3), this is the pointer you get as third parameter. diff --git a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md index 2fdf8ee15f57..13dd45077dcc 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md @@ -36,10 +36,10 @@ behaviors. Available bits: 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 +the SSL layer libcurl uses may use a workaround 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 +**WARNING:** avoiding this workaround 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. @@ -56,7 +56,7 @@ Tells libcurl to not accept "partial" certificate chains, which it otherwise does by default. This option fails the certificate verification if the chain ends with an intermediate certificate and not with a root cert. -Works with OpenSSL and its forks (LibreSSL, BoringSSL, etc). (Added in 7.68.0) +Works with OpenSSL and its forks (BoringSSL, LibreSSL, etc). (Added in 7.68.0) Works with Schannel if the user specified certificates to verify the peer. (Added in 8.15.0) @@ -76,9 +76,9 @@ verification. 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. -Works with wolfSSL on Windows, Linux (Debian, Ubuntu, Gentoo, Fedora, RHEL), +Works with wolfSSL on Windows, Linux (Debian, Fedora, Gentoo, RHEL, Ubuntu), 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). +OpenSSL and its forks (BoringSSL, LibreSSL, 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 @@ -98,13 +98,13 @@ could be a privacy violation and unexpected. ## CURLSSLOPT_EARLYDATA 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. +is supported for GnuTLS, OpenSSL, quictls and wolfSSL (but not AWS-LC or +BoringSSL). It works on TCP and QUIC connections using ngtcp2. This option works on a best effort basis, 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) +(Added in 8.11.0 for GnuTLS and 8.13.0 for OpenSSL, quictls and wolfSSL) # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_STDERR.md b/docs/libcurl/opts/CURLOPT_STDERR.md index 213eb9ef0e8c..9294fafa60a0 100644 --- a/docs/libcurl/opts/CURLOPT_STDERR.md +++ b/docs/libcurl/opts/CURLOPT_STDERR.md @@ -33,7 +33,7 @@ data. If you are using libcurl as a Windows DLL, this option causes an exception and a crash in the library since it cannot access a FILE * passed on from the -application. A work-around is to instead use CURLOPT_DEBUGFUNCTION(3). +application. A workaround is to instead use CURLOPT_DEBUGFUNCTION(3). # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md index 5e3e177dce8f..e0e8ef0c53c3 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md @@ -44,6 +44,10 @@ an error. It must be another easy handle, and it also needs to be a handle of a transfer that is about to be sent over the same HTTP/2 connection for this option to have an actual effect. +Since version 8.21.0 setting this option no longer has an effect. HTTP/2 +stream dependencies were introduced in RFC 7540 and then later deprecated +in RFC 9113. + # DEFAULT NULL @@ -69,6 +73,10 @@ int main(void) } ~~~ +# DEPRECATED + +Deprecated since 8.21.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md index fe95ae8d7101..fe8f97f950d8 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md @@ -47,6 +47,10 @@ an error. It must be another easy handle, and it also needs to be a handle of a transfer that is about to be sent over the same HTTP/2 connection for this option to have an actual effect. +Since version 8.21.0 setting this option no longer has an effect. HTTP/2 +stream dependencies were introduced in RFC 7540 and then later deprecated +in RFC 9113. + # DEFAULT NULL @@ -72,6 +76,10 @@ int main(void) } ~~~ +# DEPRECATED + +Deprecated since 8.21.0. + # %AVAILABILITY% # RETURN VALUE diff --git a/docs/libcurl/opts/CURLOPT_URL.md b/docs/libcurl/opts/CURLOPT_URL.md index 2ad4739391d8..b9e5e52b4f36 100644 --- a/docs/libcurl/opts/CURLOPT_URL.md +++ b/docs/libcurl/opts/CURLOPT_URL.md @@ -44,7 +44,7 @@ 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*. -If the given URL is missing a scheme name (such as "http://" or "ftp://" etc) +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. Scheme guessing can be disabled by setting a default @@ -111,7 +111,7 @@ are part of the regular URL format. The combination of a local host and a custom port number can allow external users to play tricks with your local services. -Accepting external URLs may also use other protocols than http:// or other +Accepting external URLs may also use other protocols than `http://` or other common ones. Restrict what accept with CURLOPT_PROTOCOLS_STR(3). User provided URLs can also be made to point to sites that redirect further on diff --git a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md index 407484debb27..792ed3d3b8ff 100644 --- a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md @@ -47,6 +47,11 @@ defined in the curl.h header file: *CURL_MAX_WRITE_SIZE* (the usual default is the write callback, you can get up to *CURL_MAX_HTTP_HEADER* bytes of header data passed into it. This usually means 100K. +The CURLOPT_WRITEFUNCTION(3) callback receives the final response payload. +When CURLOPT_FOLLOWLOCATION(3) is enabled, libcurl automatically handles +intermediate 3xx redirects, meaning their HTTP bodies are skipped and not +passed to this callback. + This function may be called with zero bytes data if the transferred file is empty. @@ -60,16 +65,13 @@ aborted and the libcurl function used returns *CURLE_WRITE_ERROR*. You can also abort the transfer by returning CURL_WRITEFUNC_ERROR (added in 7.87.0), which makes *CURLE_WRITE_ERROR* get returned. -If the callback function returns CURL_WRITEFUNC_PAUSE it pauses this -transfer. See curl_easy_pause(3) for further details. +If the callback function returns CURL_WRITEFUNC_PAUSE it pauses this transfer. +See curl_easy_pause(3) for further details. Set this option to NULL to get the internal default function used instead of your callback. The internal default function writes the data to the FILE * given with CURLOPT_WRITEDATA(3). -This option does not enable HSTS, you need to use CURLOPT_HSTS_CTRL(3) to -do that. - # DEFAULT fwrite(3) diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 0d775fa65594..5bad9a98425d 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -205,7 +205,7 @@ CURLAUTH_AWS_SIGV4 7.75.0 CURLAUTH_BASIC 7.10.6 CURLAUTH_BEARER 7.61.0 CURLAUTH_DIGEST 7.10.6 -CURLAUTH_DIGEST_IE 7.19.3 +CURLAUTH_DIGEST_IE 7.19.3 8.21.0 CURLAUTH_GSSAPI 7.55.0 CURLAUTH_GSSNEGOTIATE 7.10.6 7.38.0 CURLAUTH_NEGOTIATE 7.38.0 @@ -882,8 +882,8 @@ CURLOPT_SSLKEYPASSWD 7.9.3 7.17.0 CURLOPT_SSLKEYTYPE 7.9.3 CURLOPT_SSLVERSION 7.1 CURLOPT_STDERR 7.1 -CURLOPT_STREAM_DEPENDS 7.46.0 -CURLOPT_STREAM_DEPENDS_E 7.46.0 +CURLOPT_STREAM_DEPENDS 7.46.0 8.21.0 +CURLOPT_STREAM_DEPENDS_E 7.46.0 8.21.0 CURLOPT_STREAM_WEIGHT 7.46.0 CURLOPT_SUPPRESS_CONNECT_HEADERS 7.54.0 CURLOPT_TCP_FASTOPEN 7.49.0 @@ -993,6 +993,7 @@ CURLPROXY_HTTP 7.10 CURLPROXY_HTTP_1_0 7.19.4 CURLPROXY_HTTPS 7.52.0 CURLPROXY_HTTPS2 8.1.0 +CURLPROXY_HTTPS3 8.21.0 CURLPROXY_SOCKS4 7.10 CURLPROXY_SOCKS4A 7.18.0 CURLPROXY_SOCKS5 7.10 diff --git a/docs/libcurl/symbols.pl b/docs/libcurl/symbols.pl index 4126fe54e1a6..2717ac3a5550 100755 --- a/docs/libcurl/symbols.pl +++ b/docs/libcurl/symbols.pl @@ -50,7 +50,7 @@ open F, ") { if(/^(CURL[^ ]*)[ \t]*(.*)/) { - my ($sym, $vers)=($1, $2); + my ($sym, $vers) = ($1, $2); my $intr; my $rm; @@ -77,11 +77,11 @@ sub str2num { # is there removed info? if($vers =~ /([\d.]+)[ \t-]+([\d.-]+)[ \t]+([\d.]+)/) { - ($intr, $dep, $rm)=($1, $2, $3); + ($intr, $dep, $rm) = ($1, $2, $3); } # is it a dep-only line? elsif($vers =~ /([\d.]+)[ \t-]+([\d.]+)/) { - ($intr, $dep)=($1, $2); + ($intr, $dep) = ($1, $2); } else { $intr = $vers; diff --git a/docs/options-in-versions b/docs/options-in-versions index 95d84a4bfec2..fa20b2dd29b2 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -177,6 +177,7 @@ --proxy-digest 7.12.0 --proxy-header 7.37.0 --proxy-http2 8.1.0 +--proxy-http3 8.21.0 --proxy-insecure 7.52.0 --proxy-key 7.52.0 --proxy-key-type 7.52.0 diff --git a/docs/runtests.md b/docs/runtests.md index 79066d8ffd0b..a1190004afc7 100644 --- a/docs/runtests.md +++ b/docs/runtests.md @@ -164,11 +164,6 @@ CPU cores is a good figure to start with, or 1.3 times if Valgrind is in use, or 5 times for torture tests. Enabling parallel tests is not recommended in conjunction with the -g option. -## `-k` - -Keep output and log files in log/ after a test run, even if no error was -detected. Useful for debugging. - ## `-L \` Load and execute the specified file which should contain perl code. This diff --git a/docs/tests/FILEFORMAT.md b/docs/tests/FILEFORMAT.md index f3121fd3c9ad..b19cd7f87f96 100644 --- a/docs/tests/FILEFORMAT.md +++ b/docs/tests/FILEFORMAT.md @@ -200,8 +200,8 @@ Available substitute variables include: - `%SOCKSPORT` - Port number of the SOCKS4/5 server - `%SOCKSUNIXPATH` - Path to the Unix socket of the SOCKS server - `%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 +- `%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 @@ -487,7 +487,7 @@ Features testable here are: - `brotli` - `c-ares` - c-ares is used for (all) name resolves - `CharConv` -- `codeset-utf8`. If the running codeset is UTF-8 capable. +- `codeset-utf8` - if the running codeset is UTF-8 capable. - `cookies` - `crypto` - `cygwin` @@ -509,13 +509,13 @@ Features testable here are: - `IPv6` - `Kerberos` - `Largefile` -- `large-time` (time_t is larger than 32-bit) -- `large-size` (size_t is larger than 32-bit) +- `large-time` - time_t is larger than 32-bit +- `large-size` - size_t is larger than 32-bit - `libssh2` - `libssh` -- `badlibssh` (libssh configuration incompatible with the test suite) +- `badlibssh` - libssh configuration incompatible with the test suite - `libz` -- `local-http`. The HTTP server runs on 127.0.0.1 +- `local-http` - the HTTP server runs on 127.0.0.1 - `manual` - `mbedtls` - `Mime` @@ -539,6 +539,7 @@ Features testable here are: - `SSPI` - `threaded-resolver` - `TLS-SRP` +- `torture` - if runtests is running in memory test mode - `TrackMemory` - `typecheck` - `threadsafe` diff --git a/docs/tests/HTTP.md b/docs/tests/HTTP.md index 88fb9a0c4b4e..79f3fac20054 100644 --- a/docs/tests/HTTP.md +++ b/docs/tests/HTTP.md @@ -62,6 +62,9 @@ Via curl's `configure` script you may specify: * `--with-test-nghttpx=` if you have nghttpx to use somewhere outside your `$PATH`. + * `--with-test-h2o=` if you have h2o 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. diff --git a/docs/tests/TEST-SUITE.md b/docs/tests/TEST-SUITE.md index dc7671e2e651..6856eb5f8a21 100644 --- a/docs/tests/TEST-SUITE.md +++ b/docs/tests/TEST-SUITE.md @@ -136,7 +136,7 @@ set to identify the IP address and port number of the DNS server to use. 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 + 1.26.0 or later, gets a special workaround. 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, @@ -188,6 +188,9 @@ 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. +If a specific test takes a long time to run in memory test mode, you can +disable it individually by adding `!torture` to its `` section. + ### Debug If a test case fails, you can conveniently get the script to invoke the diff --git a/include/curl/curl.h b/include/curl/curl.h index f38bf97cba6e..31401d2db8c9 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -253,7 +253,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp, #ifndef CURL_MAX_READ_SIZE /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ -#define CURL_MAX_READ_SIZE (10*1024*1024) +#define CURL_MAX_READ_SIZE (10 * 1024 * 1024) #endif #ifndef CURL_MAX_WRITE_SIZE @@ -270,7 +270,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp, /* 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 causes reallocs infinitely */ -#define CURL_MAX_HTTP_HEADER (100*1024) +#define CURL_MAX_HTTP_HEADER (100 * 1024) #endif /* This is a magic return code for the write callback that, when returned, @@ -594,7 +594,7 @@ typedef enum { CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind that failed */ - CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialize ENGINE */ CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not accepted and we failed to login */ CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ @@ -803,9 +803,11 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ #define CURLPROXY_SOCKS5_HOSTNAME 7L /* Use the SOCKS5 protocol but pass along the hostname rather than the IP address. added in 7.18.0 */ +#define CURLPROXY_HTTPS3 8L /* HTTPS and attempt HTTP/3 + added in 8.21.0 */ typedef enum { - CURLPROXY_LAST = 8 /* never use */ + CURLPROXY_LAST = 9 /* never use */ } curl_proxytype; /* this enum was added in 7.10 */ /* @@ -817,7 +819,7 @@ typedef enum { * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) * CURLAUTH_NTLM - HTTP NTLM authentication - * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour + * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavor * 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 @@ -903,16 +905,16 @@ enum curl_khmatch { }; typedef int - (*curl_sshkeycallback) (CURL *easy, /* easy handle */ - const struct curl_khkey *knownkey, /* known */ - const struct curl_khkey *foundkey, /* found */ - enum curl_khmatch, /* libcurl's view on the keys */ - void *clientp); /* custom pointer passed with */ - /* CURLOPT_SSH_KEYDATA */ + (*curl_sshkeycallback)(CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed with */ + /* CURLOPT_SSH_KEYDATA */ typedef int - (*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed */ - /* with CURLOPT_SSH_HOSTKEYDATA */ + (*curl_sshhostkeycallback)(void *clientp,/* custom pointer passed */ + /* with CURLOPT_SSH_HOSTKEYDATA */ int keytype, /* CURLKHTYPE */ const char *key, /* hostkey to check */ size_t keylen); /* length of the key */ @@ -933,7 +935,7 @@ typedef enum { /* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the name of improving interoperability with older servers. Some SSL libraries - have introduced work-arounds for this flaw but those work-arounds sometimes + have introduced workarounds for this flaw but those workarounds 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) @@ -1363,7 +1365,7 @@ typedef enum { CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63, 8.17.0, "removed"), - /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + /* Set if we should verify the peer in SSL handshake, set 1 to verify. */ CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64), /* The CApath or CAfile used to validate the peer certificate @@ -1421,7 +1423,7 @@ typedef enum { */ CURLOPT(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80), - /* Set if we should verify the Common name from the peer certificate in ssl + /* Set if we should verify the Common name from the peer certificate in SSL * handshake, set 1 to check existence, 2 to ensure that it matches the * provided hostname. */ CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81), @@ -1495,8 +1497,8 @@ typedef enum { CURLOPT(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100), /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), - CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and - CURLPROXY_SOCKS5. */ + CURLPROXY_HTTPS, CURLPROXY_HTTPS2, CURLPROXY_HTTPS3, CURLPROXY_SOCKS4, + CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ CURLOPT(CURLOPT_PROXYTYPE, CURLOPTTYPE_VALUES, 101), /* Set the Accept-Encoding string. Use this to tell a server you would like @@ -1525,12 +1527,12 @@ typedef enum { Note that setting multiple bits may cause extra network round-trips. */ CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_VALUES, 107), - /* Set the ssl context callback function, currently only for OpenSSL or + /* Set the SSL context callback function, currently only for OpenSSL or wolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument. The function must match the curl_ssl_ctx_callback prototype. */ CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108), - /* Set the userdata for the ssl context callback function's third + /* Set the userdata for the SSL context callback function's third argument */ CURLOPT(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_CBPOINT, 109), @@ -1936,11 +1938,11 @@ typedef enum { /* Set authentication options directly */ CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224), - /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + /* Enable/disable TLS NPN extension (http2 over SSL might fail without) */ CURLOPTDEPRECATED(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225, 7.86.0, "Has no function"), - /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + /* Enable/disable TLS ALPN extension (http2 over SSL might fail without) */ CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226), /* Time to wait for a response to an HTTP request containing an @@ -1986,10 +1988,12 @@ typedef enum { CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239), /* Set stream dependency on another curl handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240), + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240, + 8.21.0, "Has no function"), /* Set E-xclusive stream dependency on another curl handle */ - CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241), + CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241, + 8.21.0, "Has no function"), /* Do not send any tftp option requests to the server */ CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242), @@ -2013,11 +2017,11 @@ typedef enum { this option is used only if PROXY_SSL_VERIFYPEER is true */ CURLOPT(CURLOPT_PROXY_CAPATH, CURLOPTTYPE_STRINGPOINT, 247), - /* Set if we should verify the proxy in ssl handshake, + /* Set if we should verify the proxy in SSL handshake, set 1 to verify. */ CURLOPT(CURLOPT_PROXY_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 248), - /* Set if we should verify the Common name from the proxy certificate in ssl + /* Set if we should verify the Common name from the proxy certificate in SSL * handshake, set 1 to check existence, 2 to ensure that it matches * the provided hostname. */ CURLOPT(CURLOPT_PROXY_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 249), @@ -2809,14 +2813,14 @@ struct curl_slist { * backend can also be specified via the name parameter (passing -1 as id). If * 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 + * and set the "avail" pointer to the null-terminated list of available * backends. * * Upon success, the function returns CURLSSLSET_OK. * * If the specified SSL backend is not available, the function returns * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a - * NULL-terminated list of available SSL backends. + * 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 results in a CURLSSLSET_TOO_LATE. diff --git a/include/curl/curlver.h b/include/curl/curlver.h index 144f5fea17b3..5cc0e27c61dd 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,12 +32,12 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.20.0-DEV" +#define LIBCURL_VERSION "8.21.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 20 +#define LIBCURL_VERSION_MINOR 21 #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 always @@ -47,7 +47,7 @@ Where XX, YY and ZZ are the main version, release and patch numbers in hexadecimal (using 8 bits each). All three numbers are always represented - using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + using two digits. Version 1.2 would appear as "0x010200" while 9.11.7 appears as "0x090b07". This 6-digit (24 bits) hexadecimal number does not show pre-release number, @@ -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 0x081400 +#define LIBCURL_VERSION_NUM 0x081500 /* * This is the date and time when the full source package was created. The diff --git a/include/curl/system.h b/include/curl/system.h index c2dbab56e089..30216ea34c10 100644 --- a/include/curl/system.h +++ b/include/curl/system.h @@ -297,7 +297,7 @@ /* ===================================== */ #elif defined(_MSC_VER) -# if (_MSC_VER >= 1800) +# if _MSC_VER >= 1800 # include # define CURL_FORMAT_CURL_OFF_T PRId64 # define CURL_FORMAT_CURL_OFF_TU PRIu64 diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index aae466c677bd..3ef623536e97 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -110,8 +110,7 @@ if(SHARE_LIB_OBJECT) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB") endif() target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS}) - set_target_properties(${LIB_OBJECT} PROPERTIES - POSITION_INDEPENDENT_CODE ON) + 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_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}") @@ -253,7 +252,7 @@ if(BUILD_SHARED_LIBS) CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR CMAKE_SYSTEM_NAME STREQUAL "OHOS" OR # OpenHarmony CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR - # FreeBSD comes with the a.out and ELF flavours but a.out was supported + # FreeBSD comes with the a.out and ELF flavors but a.out was supported # up to v3.x and ELF from v3.x. I cannot imagine someone running CMake # on those ancient systems. CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") diff --git a/lib/Makefile.inc b/lib/Makefile.inc index d762f72e42c9..266ba52af377 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -96,6 +96,7 @@ LIB_VTLS_CFILES = \ vtls/schannel.c \ vtls/schannel_verify.c \ vtls/vtls.c \ + vtls/vtls_config.c \ vtls/vtls_scache.c \ vtls/vtls_spack.c \ vtls/wolfssl.c \ @@ -113,23 +114,32 @@ LIB_VTLS_HFILES = \ vtls/schannel.h \ vtls/schannel_int.h \ vtls/vtls.h \ + vtls/vtls_config.h \ vtls/vtls_int.h \ vtls/vtls_scache.h \ vtls/vtls_spack.h \ vtls/wolfssl.h \ vtls/x509asn1.h -LIB_VQUIC_CFILES = \ - vquic/curl_ngtcp2.c \ - vquic/curl_quiche.c \ - vquic/vquic.c \ +LIB_VQUIC_CFILES = \ + vquic/capsule.c \ + vquic/cf-capsule.c \ + vquic/cf-ngtcp2.c \ + vquic/cf-ngtcp2-cmn.c \ + vquic/cf-ngtcp2-proxy.c \ + vquic/cf-quiche.c \ + vquic/vquic.c \ vquic/vquic-tls.c -LIB_VQUIC_HFILES = \ - vquic/curl_ngtcp2.h \ - vquic/curl_quiche.h \ - vquic/vquic.h \ - vquic/vquic_int.h \ +LIB_VQUIC_HFILES = \ + vquic/capsule.h \ + vquic/cf-capsule.h \ + vquic/cf-ngtcp2.h \ + vquic/cf-ngtcp2-cmn.h \ + vquic/cf-ngtcp2-proxy.h \ + vquic/cf-quiche.h \ + vquic/vquic.h \ + vquic/vquic_int.h \ vquic/vquic-tls.h LIB_VSSH_CFILES = \ @@ -155,12 +165,15 @@ LIB_CFILES = \ cf-haproxy.c \ cf-https-connect.c \ cf-ip-happy.c \ + cf-recvbuf.c \ + cf-setup.c \ cf-socket.c \ cfilters.c \ conncache.c \ connect.c \ content_encoding.c \ cookie.c \ + creds.c \ cshutdn.c \ curl_addrinfo.c \ curl_endian.c \ @@ -230,13 +243,14 @@ LIB_CFILES = \ multi_ev.c \ multi_ntfy.c \ netrc.c \ - noproxy.c \ openldap.c \ parsedate.c \ + peer.c \ pingpong.c \ pop3.c \ progress.c \ protocol.c \ + proxy.c \ psl.c \ rand.c \ ratelimit.c \ @@ -285,6 +299,8 @@ LIB_HFILES = \ cf-haproxy.h \ cf-https-connect.h \ cf-ip-happy.h \ + cf-recvbuf.h \ + cf-setup.h \ cf-socket.h \ cfilters.h \ conncache.h \ @@ -292,6 +308,7 @@ LIB_HFILES = \ connect.h \ content_encoding.h \ cookie.h \ + creds.h \ curl_addrinfo.h \ curl_ctype.h \ curl_endian.h \ @@ -362,12 +379,13 @@ LIB_HFILES = \ multi_ntfy.h \ multiif.h \ netrc.h \ - noproxy.h \ parsedate.h \ + peer.h \ pingpong.h \ pop3.h \ progress.h \ protocol.h \ + proxy.h \ psl.h \ rand.h \ ratelimit.h \ diff --git a/lib/altsvc.c b/lib/altsvc.c index 81b5379ad42c..80913d9a1d8b 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -113,11 +113,11 @@ static struct altsvc *altsvc_createid(const char *srchost, return NULL; as->src.host = (char *)as + sizeof(struct altsvc); memcpy(as->src.host, srchost, hlen); - /* the null terminator is already there */ + /* the null-terminator is already there */ as->dst.host = (char *)as + sizeof(struct altsvc) + hlen + 1; memcpy(as->dst.host, dsthost, dlen); - /* the null terminator is already there */ + /* the null-terminator is already there */ as->src.alpnid = srcalpnid; as->dst.alpnid = dstalpnid; @@ -149,7 +149,7 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost, 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 */ + /* It is 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); diff --git a/lib/arpa_telnet.h b/lib/arpa_telnet.h index b5faab419c26..826f937537c0 100644 --- a/lib/arpa_telnet.h +++ b/lib/arpa_telnet.h @@ -35,6 +35,7 @@ #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 diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index a4a74642859a..d17a6038e2b9 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -281,8 +281,8 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, 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); + &ares->res_AAAA, &ares->res_A, + async->hostname, async->port); if(!dns) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -314,7 +314,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, } CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound", - result, *pdns ? "" : "not "); + (int)result, *pdns ? "" : "not "); async_ares_cleanup(async); out: @@ -489,7 +489,7 @@ static struct Curl_addrinfo *async_ares_node2addr( struct Curl_addrinfo *calast = NULL; int error = 0; - for(ai = node; ai != NULL; ai = ai->ai_next) { + for(ai = node; ai; ai = ai->ai_next) { size_t ss_size; struct Curl_addrinfo *ca; /* ignore elements with unsupported address family, diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 90f055c2a159..aeadec11a4de 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -111,7 +111,7 @@ struct async_thrdd_item { #ifdef CURLVERBOSE char description[CURL_ASYN_ITEM_DESC_LEN]; #endif - int sock_error; + int sockerr; uint32_t mid; uint32_t resolv_id; uint16_t port; @@ -372,9 +372,9 @@ static void async_thrdd_item_process(void *arg) 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; + item->sockerr = SOCKERRNO ? SOCKERRNO : rc; + if(item->sockerr == 0) + item->sockerr = RESOLVER_ENOMEM; } else { Curl_addrinfo_set_port(item->res, item->port); @@ -399,9 +399,9 @@ static void async_thrdd_item_process(void *arg) #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; + item->sockerr = SOCKERRNO; + if(item->sockerr == 0) + item->sockerr = RESOLVER_ENOMEM; } } @@ -481,8 +481,13 @@ static void async_thrdd_report_item(struct Curl_easy *data, 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; + int ai_family; +#ifdef USE_IPV6 + ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; +#else + ai_family = AF_INET; +#endif if(!CURL_TRC_DNS_is_verbose(data)) return; @@ -635,7 +640,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, out: if(result) CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", - async->hostname, async->port, result); + async->hostname, async->port, (int)result); return result; } @@ -701,6 +706,9 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, if(thrdd->rr.channel) (void)Curl_ares_perform(thrdd->rr.channel, 0); #endif +#ifndef ENABLE_WAKEUP + Curl_async_thrdd_multi_process(data->multi); +#endif if(!async->done) return CURLE_AGAIN; @@ -756,7 +764,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data, (result != CURLE_COULDNT_RESOLVE_HOST) && (result != CURLE_COULDNT_RESOLVE_PROXY)) { CURL_TRC_DNS(data, "Error %d resolving %s:%d", - result, async->hostname, async->port); + (int)result, async->hostname, async->port); } return result; } @@ -789,10 +797,12 @@ const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, if(thrdd->res_A) return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index); break; +#ifdef USE_IPV6 case AF_INET6: if(thrdd->res_AAAA) return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index); break; +#endif default: break; } diff --git a/lib/bufq.h b/lib/bufq.h index da411b586d86..c53749d84e36 100644 --- a/lib/bufq.h +++ b/lib/bufq.h @@ -204,6 +204,7 @@ void Curl_bufq_skip(struct bufq *q, size_t amount); typedef CURLcode Curl_bufq_writer(void *writer_ctx, const uint8_t *buf, size_t len, size_t *pwritten); + /** * Passes the chunks in the buffer queue to the writer and returns * the amount of buf written. A writer may return -1 and CURLE_AGAIN diff --git a/lib/cf-dns.c b/lib/cf-dns.c index e763b8ed3849..631c987be262 100644 --- a/lib/cf-dns.c +++ b/lib/cf-dns.c @@ -37,48 +37,38 @@ struct cf_dns_ctx { struct Curl_dns_entry *dns; + struct Curl_peer *peer; 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); BIT(for_proxy); - char hostname[1]; }; static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, - bool abstract_unix_socket, + uint8_t transport, bool for_proxy, - bool complete_resolve, - struct Curl_dns_entry *dns) + bool complete_resolve) { struct cf_dns_ctx *ctx; - size_t hlen = strlen(hostname); - ctx = curlx_calloc(1, sizeof(*ctx) + hlen); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) return NULL; - ctx->port = port; + Curl_peer_link(&ctx->peer, peer); ctx->dns_queries = dns_queries; ctx->transport = transport; - ctx->abstract_unix_socket = abstract_unix_socket; ctx->for_proxy = for_proxy; 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); + peer->hostname, peer->port, ctx->transport, ctx->dns_queries); return ctx; } @@ -86,6 +76,7 @@ static void cf_dns_ctx_destroy(struct Curl_easy *data, struct cf_dns_ctx *ctx) { if(ctx) { + Curl_peer_unlink(&ctx->peer); Curl_dns_entry_unlink(data, &ctx->dns); curlx_free(ctx); } @@ -131,16 +122,14 @@ static void cf_dns_report(struct Curl_cfilter *cf, !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) return; - switch(ctx->transport) { - case TRNSPRT_UNIX: + if(ctx->peer->unix_socket) { #ifdef USE_UNIX_SOCKETS - CURL_TRC_CF(data, cf, "resolved unix domain %s", - Curl_conn_get_unix_path(data->conn)); + CURL_TRC_CF(data, cf, "resolved unix://%s", ctx->peer->hostname); #else DEBUGASSERT(0); #endif - break; - default: + } + else { curlx_dyn_init(&tmp, 1024); infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); #ifdef CURLRES_IPV6 @@ -161,7 +150,6 @@ static void cf_dns_report(struct Curl_cfilter *cf, } #endif curlx_dyn_free(&tmp); - break; } } #else @@ -181,24 +169,19 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, *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)) + CURL_TRC_CF(data, cf, "cf_dns_start %s %s:%u", + ctx->peer->unix_socket ? "unix-domain-socket" : "host", + ctx->peer->hostname, ctx->peer->port); + if(ctx->peer->unix_socket) + ctx->dns_queries = 0; + else if(Curl_is_ipv4addr(ctx->peer->hostname)) ctx->dns_queries |= CURL_DNSQ_A; #ifdef USE_IPV6 - else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */ + else if(ctx->peer->ipv6) ctx->dns_queries |= CURL_DNSQ_AAAA; #endif - result = Curl_resolv(data, ctx->dns_queries, - ctx->hostname, ctx->port, ctx->transport, + + result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport, (bool)ctx->for_proxy, timeout_ms, &ctx->resolv_id, pdns); DEBUGASSERT(!result || !*pdns); @@ -211,14 +194,14 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf, } else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ failf(data, "Failed to resolve '%s' with timeout after %" - FMT_TIMEDIFF_T " ms", ctx->hostname, + FMT_TIMEDIFF_T " ms", ctx->peer->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); + failf(data, "Could not resolve: %s", ctx->peer->hostname); return result; } } @@ -281,7 +264,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf, } if(ctx->resolv_result) { - CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result); + CURL_TRC_CF(data, cf, "error resolving: %d", (int)ctx->resolv_result); return ctx->resolv_result; } @@ -327,13 +310,6 @@ static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 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) @@ -379,7 +355,6 @@ struct Curl_cftype Curl_cft_dns = { 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, @@ -393,23 +368,19 @@ struct Curl_cftype Curl_cft_dns = { static CURLcode cf_dns_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, - bool abstract_unix_socket, bool for_proxy, - bool complete_resolve, - struct Curl_dns_entry *dns) + bool complete_resolve) { 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, for_proxy, - complete_resolve, dns); + ctx = cf_dns_ctx_create(data, peer, dns_queries, transport, + for_proxy, complete_resolve); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -424,88 +395,28 @@ static CURLcode cf_dns_create(struct Curl_cfilter **pcf, 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, for_proxy = 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->bits.proxy) { - for_proxy = TRUE; - hostname = conn->bits.socksproxy ? - conn->socks_proxy.host.name : conn->http_proxy.host.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, for_proxy, - 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. - */ + * The filter will resolve the peer on the first connect attempt. */ CURLcode Curl_cf_dns_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, + struct Curl_peer *peer, uint8_t dns_queries, - uint8_t transport, - struct Curl_dns_entry *dns) + uint8_t transport) { struct Curl_cfilter *cf = NULL; + bool for_proxy = FALSE; 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, FALSE, dns); - } - else { - DEBUGASSERT(0); - result = CURLE_FAILED_INIT; - } + if(!peer) + return CURLE_FAILED_INIT; +#ifndef CURL_DISABLE_PROXY + for_proxy = (peer == conn->socks_proxy.peer) || + (peer == conn->http_proxy.peer); +#endif + + result = cf_dns_create(&cf, data, peer, dns_queries, transport, + for_proxy, FALSE); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -514,7 +425,7 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, } /* Insert a new "resolv" filter directly after `cf`. It will - * start a DNS resolve for the given hostnmae and port on the + * start a DNS resolve for the given peer on the * first connect attempt. * See socks.c on how this is used to make a non-blocking DNS * resolve during connect. @@ -522,17 +433,15 @@ CURLcode Curl_cf_dns_add(struct Curl_easy *data, 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, + struct Curl_peer *peer, uint8_t transport, bool complete_resolve) { struct Curl_cfilter *cf; CURLcode result; - result = cf_dns_create(&cf, data, dns_queries, - hostname, port, transport, - FALSE, FALSE, complete_resolve, NULL); + result = cf_dns_create(&cf, data, peer, dns_queries, transport, + FALSE, complete_resolve); if(result) return result; @@ -543,29 +452,35 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, /* Return the resolv result from the first "resolv" filter, starting * the given filter `cf` downwards. */ -static CURLcode cf_dns_result(struct Curl_cfilter *cf) +static CURLcode cf_dns_result(struct Curl_cfilter *cf, + struct Curl_peer *peer) { 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; + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->dns || ctx->resolv_result) + return ctx->resolv_result; + return CURLE_AGAIN; + } + return CURLE_OK; /* ok, but no results */ } } return CURLE_FAILED_INIT; } -/* Return the result of the DNS resolution. Searches for a "resolv" +/* Return the result of the DNS resolution for peer. 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) +CURLcode Curl_conn_dns_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer) { - return cf_dns_result(conn->cfilter[sockindex]); + return cf_dns_result(conn->cfilter[sockindex], peer); } static const struct Curl_addrinfo *cf_dns_get_nth_ai( @@ -598,6 +513,7 @@ static const struct Curl_addrinfo *cf_dns_get_nth_ai( */ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_peer *peer, int ai_family, unsigned int index) { @@ -605,12 +521,14 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, 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); + if(Curl_peer_same_destination(ctx->peer, peer)) { + 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; @@ -621,11 +539,14 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, * 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, + struct Curl_peer *peer, + 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); + return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, peer, + ai_family, index); } #ifdef USE_HTTPSRR @@ -633,35 +554,43 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, * 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) +const struct Curl_https_rrinfo * +Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer) { 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); + if(Curl_peer_same_destination(ctx->peer, peer)) { + 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) +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer) { 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); + if(Curl_peer_same_destination(ctx->peer, peer)) { + if(ctx->dns) + return TRUE; + else + return Curl_resolv_knows_https(data, ctx->resolv_id); + } } } - return FALSE; + return TRUE; } #endif /* USE_HTTPSRR */ diff --git a/lib/cf-dns.h b/lib/cf-dns.h index 3c46b1bf3dc5..891b1efea606 100644 --- a/lib/cf-dns.h +++ b/lib/cf-dns.h @@ -29,41 +29,49 @@ struct Curl_easy; struct connectdata; struct Curl_dns_entry; struct Curl_addrinfo; +struct Curl_peer; CURLcode Curl_cf_dns_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, + struct Curl_peer *peer, uint8_t dns_queries, - uint8_t transport, - struct Curl_dns_entry *dns); + uint8_t transport); 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, + struct Curl_peer *peer, uint8_t transport, bool complete_resolve); -CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); +CURLcode Curl_conn_dns_result(struct connectdata *conn, + int sockindex, + struct Curl_peer *peer); const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + struct Curl_peer *peer, 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, + struct Curl_peer *peer, 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); +const struct Curl_https_rrinfo * +Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer); +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, + int sockindex, + struct Curl_peer *peer); #else -#define Curl_conn_dns_get_https(a, b) NULL -#define Curl_conn_dns_resolved_https(a, b) TRUE +#define Curl_conn_dns_get_https(a, b, c) NULL +#define Curl_conn_dns_resolved_https(a, b, c) TRUE #endif extern struct Curl_cftype Curl_cft_dns; diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index 3c2c8374d14b..f1e5c7a42ab2 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -25,6 +25,8 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include #include "urldata.h" #include "curlx/dynbuf.h" #include "sendf.h" @@ -33,6 +35,7 @@ #include "http_proxy.h" #include "select.h" #include "progress.h" +#include "multiif.h" #include "cfilters.h" #include "cf-h1-proxy.h" #include "connect.h" @@ -40,7 +43,6 @@ #include "strcase.h" #include "curlx/strparse.h" - typedef enum { H1_TUNNEL_INIT, /* init/default/no tunnel state */ H1_TUNNEL_CONNECT, /* CONNECT request is being send */ @@ -52,11 +54,13 @@ typedef enum { /* struct for HTTP CONNECT tunneling */ struct h1_tunnel_state { + struct Curl_peer *dest; struct dynbuf rcvbuf; struct dynbuf request_data; size_t nsent; size_t headerlines; struct Curl_chunker ch; + int httpversion; enum keeponval { KEEPON_DONE, KEEPON_CONNECT, @@ -70,6 +74,12 @@ struct h1_tunnel_state { BIT(leading_unfold); }; +/* Persistent context for the H1-PROXY filter */ +struct cf_h1_proxy_ctx { + struct h1_tunnel_state *ts; + BIT(udp_tunnel); +}; + static bool tunnel_is_established(struct h1_tunnel_state *ts) { return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); @@ -80,6 +90,12 @@ static bool tunnel_is_failed(struct h1_tunnel_state *ts) return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); } +static bool h1_proxy_is_udp(struct Curl_cfilter *cf) +{ + struct cf_h1_proxy_ctx *pctx = cf->ctx; + return (pctx->udp_tunnel ? TRUE : FALSE); +} + static CURLcode tunnel_reinit(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts) @@ -95,6 +111,8 @@ static CURLcode tunnel_reinit(struct Curl_cfilter *cf, ts->close_connection = FALSE; ts->maybe_folded = FALSE; ts->leading_unfold = FALSE; + ts->nsent = 0; + ts->headerlines = 0; return CURLE_OK; } @@ -156,7 +174,9 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, case H1_TUNNEL_ESTABLISHED: CURL_TRC_CF(data, cf, "new tunnel state 'established'"); - infof(data, "CONNECT phase completed"); + infof(data, "CONNECT%s phase completed for HTTP proxy", + h1_proxy_is_udp(cf) ? "-UDP" : ""); + data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; FALLTHROUGH(); @@ -172,23 +192,33 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf, /* 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. Let's thus free and clear it here. */ - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); break; } } -static void tunnel_free(struct Curl_cfilter *cf, +static void tunnel_free(struct h1_tunnel_state *ts, struct Curl_easy *data) +{ + if(ts) { + Curl_peer_unlink(&ts->dest); + curlx_dyn_free(&ts->rcvbuf); + curlx_dyn_free(&ts->request_data); + Curl_httpchunk_free(data, &ts->ch); + curlx_free(ts); + } +} + +static void cf_tunnel_free(struct Curl_cfilter *cf, + struct Curl_easy *data) { if(cf) { - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx ? pctx->ts : NULL; if(ts) { h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); - curlx_dyn_free(&ts->rcvbuf); - curlx_dyn_free(&ts->request_data); - Curl_httpchunk_free(data, &ts->ch); - curlx_free(ts); - cf->ctx = NULL; + tunnel_free(ts, data); + pctx->ts = NULL; } } } @@ -206,20 +236,21 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf, int http_minor; CURLcode result; + DEBUGASSERT(data); /* 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); + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, ts->dest, + PROXY_HTTP_V1, + h1_proxy_is_udp(cf)); if(result) goto out; - infof(data, "Establish HTTP proxy tunnel to %s", req->authority); - curlx_dyn_reset(&ts->request_data); ts->nsent = 0; ts->headerlines = 0; - http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; + http_minor = ts->httpversion % 10; result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); if(!result) @@ -268,6 +299,92 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, return result; } +static CURLcode on_resp_header_udp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h1_tunnel_state *ts, + const char *header) +{ + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + + 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); + if(!auth) + return CURLE_OUT_OF_MEMORY; + + CURL_TRC_CF(data, cf, "CONNECT-UDP: fwd auth header '%s'", header); + result = Curl_http_input_auth(data, proxy, auth); + + curlx_free(auth); + + if(result) + return result; + } + else if(checkprefix("Content-Length:", header)) { + if(k->httpcode / 100 == 2 || k->httpcode == 101) { + infof(data, "Ignoring Content-Length in CONNECT-UDP %03d response", + k->httpcode); + } + else { + const char *p = header + strlen("Content-Length:"); + if(curlx_str_numblanks(&p, &ts->cl)) { + failf(data, "Unsupported Content-Length value"); + return CURLE_WEIRD_SERVER_REPLY; + } + } + } + else if(checkprefix("Transfer-Encoding:", header)) { + if(k->httpcode / 100 == 2 || k->httpcode == 101) { + infof(data, "Ignoring Transfer-Encoding in " + "CONNECT-UDP %03d response", k->httpcode); + } + else if(Curl_compareheader(header, + STRCONST("Transfer-Encoding:"), + STRCONST("chunked"))) { + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> " + "Transfer-Encoding: chunked"); + ts->chunked_encoding = TRUE; + /* reset our chunky engine */ + Curl_httpchunk_reset(data, &ts->ch, TRUE); + } + } + else if(checkprefix("Capsule-protocol:", header)) { + if(Curl_compareheader(header, + STRCONST("Capsule-protocol:"), + STRCONST("?1"))) { + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> Capsule-protocol: ?1"); + } + } + else if(Curl_compareheader(header, + STRCONST("Connection:"), STRCONST("close"))) { + ts->close_connection = TRUE; + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> Connection: close"); + } + else if(Curl_compareheader(header, + STRCONST("Proxy-Connection:"), + STRCONST("close"))) { + ts->close_connection = TRUE; + CURL_TRC_CF(data, cf, + "CONNECT-UDP Response --> Proxy-Connection: close"); + } + else if(!strncmp(header, "HTTP/1.", 7) && + ((header[7] == '0') || (header[7] == '1')) && + (header[8] == ' ') && + ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && + !ISDIGIT(header[12])) { + /* store the HTTP code from the proxy */ + data->info.httpproxycode = k->httpcode = + ((header[9] - '0') * 100) + + ((header[10] - '0') * 10) + + (header[11] - '0'); + CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> %d", k->httpcode); + } + return result; +} + static CURLcode on_resp_header(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, @@ -359,6 +476,12 @@ static CURLcode single_header(struct Curl_cfilter *cf, /* output debug if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); + /* a CONNECT response line is handed to the client as a header, so it must + pass the same checks as a regular response header before delivery */ + result = Curl_verify_header(data, linep, line_len); + if(result) + return result; + /* send the header to the callback */ writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); @@ -406,7 +529,13 @@ static CURLcode single_header(struct Curl_cfilter *cf, return result; } - result = on_resp_header(cf, data, ts, linep); + if(h1_proxy_is_udp(cf)) { + result = on_resp_header_udp(cf, data, ts, linep); + } + else { + result = on_resp_header(cf, data, ts, linep); + } + if(result) return result; @@ -448,8 +577,15 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, } if(!nread) { + if(ts->maybe_folded) { + /* EOF right after LF: finalize the pending header line. */ + result = single_header(cf, data, ts); + if(result) + return result; + ts->maybe_folded = FALSE; + } if(data->set.proxyauth && data->state.authproxy.avail && - data->req.proxyuserpwd) { + data->req.hd_proxy_auth) { /* proxy auth was requested and there was proxy auth available, then deem this as "mere" proxy disconnect */ ts->close_connection = TRUE; @@ -539,12 +675,16 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, ts->maybe_folded = TRUE; } + if(result) + return result; } /* 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 && + !(h1_proxy_is_udp(cf) && data->info.httpproxycode == 101)) { /* 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); @@ -597,7 +737,8 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "CONNECT receive"); result = recv_CONNECT_resp(cf, data, ts, &done); if(result) - CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result); + CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", + (int)result); if(!result) result = Curl_pgrsUpdate(data); /* error or not complete yet. return for more multi-multi */ @@ -623,9 +764,7 @@ 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); - result = Curl_conn_cf_connect(cf->next, data, &done); - goto out; + return CURLE_AGAIN; } else { /* staying on this connection, reset state */ @@ -641,17 +780,36 @@ 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) { - /* a non-2xx response and we have no next URL to try. */ - 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_COULDNT_CONNECT; + if(h1_proxy_is_udp(cf)) { + /* RFC 9298: Accept 101 Upgrade for HTTP/1.1 and + * 2xx responses for HTTP/2 and HTTP/3 proxies. */ + if(data->info.httpproxycode / 100 != 2 && + data->info.httpproxycode != 101) { + curlx_safefree(data->req.newurl); + h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); + failf(data, "CONNECT-UDP tunnel failed, response %d", + data->req.httpcode); + return CURLE_COULDNT_CONNECT; + } + } + else { + if(data->info.httpproxycode / 100 != 2) { + /* a non-2xx response and we have no next URL to try. */ + 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_COULDNT_CONNECT; + } } /* 2xx response, SUCCESS! */ + /* 101 Switching Protocol for CONNECT-UDP */ h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); - infof(data, "CONNECT tunnel established, response %d", - data->info.httpproxycode); + if(h1_proxy_is_udp(cf)) + infof(data, "CONNECT-UDP tunnel established, response %d", + data->info.httpproxycode); + else + infof(data, "CONNECT tunnel established, response %d", + data->info.httpproxycode); result = CURLE_OK; out: @@ -665,7 +823,8 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, bool *done) { CURLcode result; - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx->ts; if(cf->connected) { *done = TRUE; @@ -682,7 +841,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, result = tunnel_init(cf, data, &ts); if(result) return result; - cf->ctx = ts; + pctx->ts = ts; } /* We want "seamless" operations through HTTP proxy tunnel */ @@ -690,18 +849,17 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, result = H1_CONNECT(cf, data, ts); if(result) goto out; - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); out: - *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); + *done = (result == CURLE_OK) && tunnel_is_established(pctx->ts); if(*done) { cf->connected = TRUE; /* The real request will follow the CONNECT, reset request partially */ Curl_req_soft_reset(&data->req, data); Curl_client_reset(data); Curl_pgrsReset(data); - - tunnel_free(cf, data); + cf_tunnel_free(cf, data); } return result; } @@ -710,7 +868,8 @@ static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) { - struct h1_tunnel_state *ts = cf->ctx; + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx->ts; CURLcode result = CURLE_OK; if(!cf->connected) { @@ -730,28 +889,52 @@ static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, else result = Curl_pollset_set_out_only(data, ps, sock); } + else { + if(cf->next) + result = cf->next->cft->adjust_pollset(cf->next, data, ps); + } return result; } +static bool cf_h1_proxy_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "destroy"); - tunnel_free(cf, data); + cf_tunnel_free(cf, data); + curlx_safefree(cf->ctx); } -static void cf_h1_proxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode cf_h1_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) { - CURL_TRC_CF(data, cf, "close"); - if(cf) { - cf->connected = FALSE; - if(cf->ctx) { - h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); + struct cf_h1_proxy_ctx *pctx = cf->ctx; + struct h1_tunnel_state *ts = pctx ? pctx->ts : NULL; + switch(query) { + case CF_QUERY_HOST_PORT: + if(!ts || !ts->dest) + break; + *pres1 = (int)ts->dest->port; + *((const char **)pres2) = ts->dest->hostname; + return CURLE_OK; + case CF_QUERY_ALPN_NEGOTIATED: { + const char **palpn = pres2; + DEBUGASSERT(palpn); + *palpn = 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_h1_proxy = { @@ -760,28 +943,62 @@ struct Curl_cftype Curl_cft_h1_proxy = { 0, cf_h1_proxy_destroy, cf_h1_proxy_connect, - cf_h1_proxy_close, Curl_cf_def_shutdown, cf_h1_proxy_adjust_pollset, - Curl_cf_def_data_pending, + cf_h1_proxy_data_pending, Curl_cf_def_send, Curl_cf_def_recv, Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_http_proxy_query, + cf_h1_proxy_query, }; CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *dest, + int httpversion, + bool udp_tunnel) { struct Curl_cfilter *cf; + struct cf_h1_proxy_ctx *pctx; + struct h1_tunnel_state *ts; CURLcode result; (void)data; - result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); - if(!result) - Curl_conn_cf_insert_after(cf_at, cf); + if(!dest) + return CURLE_FAILED_INIT; + if((httpversion < 10) || (httpversion >= 20)) + return CURLE_FAILED_INIT; + + ts = curlx_calloc(1, sizeof(*ts)); + if(!ts) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_peer_link(&ts->dest, dest); + ts->httpversion = httpversion; + curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); + curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); + Curl_httpchunk_init(data, &ts->ch, TRUE); + + pctx = curlx_calloc(1, sizeof(*pctx)); + if(!pctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + pctx->udp_tunnel = udp_tunnel; + pctx->ts = ts; + result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, pctx); + if(result) { + curlx_free(pctx); + goto out; + } + ts = NULL; + Curl_conn_cf_insert_after(cf_at, cf); + +out: + tunnel_free(ts, data); return result; } diff --git a/lib/cf-h1-proxy.h b/lib/cf-h1-proxy.h index 6544ec58d026..3255bf79f240 100644 --- a/lib/cf-h1-proxy.h +++ b/lib/cf-h1-proxy.h @@ -27,8 +27,13 @@ #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) +struct Curl_peer; + CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *dest, + int httpversion, + bool udp_tunnel); extern struct Curl_cftype Curl_cft_h1_proxy; diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 2f8cc41dd52b..be303ffd3ea3 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -76,30 +76,40 @@ struct tunnel_stream { BIT(reset); }; -static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, - struct tunnel_stream *ts) +static CURLcode tunnel_stream_init(struct tunnel_stream *ts, + struct Curl_peer *dest) { - const char *hostname; - uint16_t port; - bool ipv6_ip; - ts->state = H2_TUNNEL_INIT; ts->stream_id = -1; Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS); - Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - /* host:port with IPv6 support */ - ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, - ipv6_ip ? "]" : "", port); + ts->authority = curl_maprintf("%s%s%s:%u", dest->ipv6 ? "[" : "", + dest->hostname, + dest->ipv6 ? "]" : "", + dest->port); if(!ts->authority) return CURLE_OUT_OF_MEMORY; return CURLE_OK; } +static void tunnel_stream_reset(struct tunnel_stream *ts) +{ + Curl_http_resp_free(ts->resp); + ts->resp = NULL; + Curl_bufq_reset(&ts->recvbuf); + Curl_bufq_reset(&ts->sendbuf); + ts->stream_id = -1; + ts->error = 0; + ts->has_final_response = FALSE; + ts->closed = FALSE; + ts->reset = FALSE; + ts->state = H2_TUNNEL_INIT; +} + static void tunnel_stream_clear(struct tunnel_stream *ts) { Curl_http_resp_free(ts->resp); @@ -113,9 +123,11 @@ static void tunnel_stream_clear(struct tunnel_stream *ts) static void h2_tunnel_go_state(struct Curl_cfilter *cf, struct tunnel_stream *ts, h2_tunnel_state new_state, - struct Curl_easy *data) + struct Curl_easy *data, + bool udp_tunnel) { (void)cf; + (void)udp_tunnel; if(ts->state == new_state) return; @@ -131,7 +143,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, switch(new_state) { case H2_TUNNEL_INIT: CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id); - tunnel_stream_clear(ts); + tunnel_stream_reset(ts); break; case H2_TUNNEL_CONNECT: @@ -147,7 +159,8 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, case H2_TUNNEL_ESTABLISHED: CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'", ts->stream_id); - infof(data, "CONNECT phase completed"); + infof(data, "CONNECT%s phase completed for HTTP/2 proxy", + udp_tunnel ? "-UDP" : ""); data->state.authproxy.done = TRUE; data->state.authproxy.multipass = FALSE; FALLTHROUGH(); @@ -158,7 +171,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf, /* 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. Let's thus free and clear it here. */ - curlx_safefree(data->req.proxyuserpwd); + curlx_safefree(data->req.hd_proxy_auth); break; } } @@ -171,6 +184,7 @@ struct cf_h2_proxy_ctx { struct bufq inbufq; /* network receive buffer */ struct bufq outbufq; /* network send buffer */ + struct Curl_peer *dest; /* where to tunnel to */ struct tunnel_stream tunnel; /* our tunnel CONNECT stream */ int32_t goaway_error; int32_t last_stream_id; @@ -178,6 +192,7 @@ struct cf_h2_proxy_ctx { BIT(rcvd_goaway); BIT(sent_goaway); BIT(nw_out_blocked); + BIT(udp_tunnel); }; /* How to access `call_data` from a cf_h2 filter */ @@ -193,6 +208,7 @@ static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx) } Curl_bufq_free(&ctx->inbufq); Curl_bufq_free(&ctx->outbufq); + Curl_peer_unlink(&ctx->dest); tunnel_stream_clear(&ctx->tunnel); memset(ctx, 0, sizeof(*ctx)); ctx->call_data = save; @@ -213,7 +229,8 @@ static void drain_tunnel(struct Curl_cfilter *cf, struct cf_h2_proxy_ctx *ctx = cf->ctx; (void)cf; if(!tunnel->closed && !tunnel->reset && - !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) + (!Curl_bufq_is_empty(&ctx->tunnel.sendbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.recvbuf))) Curl_multi_mark_dirty(data); } @@ -228,7 +245,7 @@ static CURLcode proxy_h2_nw_out_writer(void *writer_ctx, CURLcode result; 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); + buflen, (int)result, *pnwritten); return result; } return CURLE_FAILED_INIT; @@ -247,8 +264,7 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf, return rc; /* We handle window updates ourself to enforce buffer limits */ nghttp2_option_set_no_auto_window_update(o, 1); -#if NGHTTP2_VERSION_NUM >= 0x013200 - /* with 1.50.0 */ +#if NGHTTP2_VERSION_NUM >= 0x013200 /* with 1.50.0 */ /* turn off RFC 9113 leading and trailing white spaces validation against HTTP field value. */ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); @@ -354,7 +370,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %d, %zu", - Curl_bufq_len(&ctx->inbufq), result, nread); + Curl_bufq_len(&ctx->inbufq), (int)result, nread); if(result) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 proxy data"); @@ -363,6 +379,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, break; } else if(nread == 0) { + CURL_TRC_CF(data, cf, "server closed connection"); ctx->conn_closed = TRUE; break; } @@ -558,12 +575,12 @@ static int proxy_h2_on_header(nghttp2_session *session, } if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && - memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { + !memcmp(HTTP_PSEUDO_STATUS, name, namelen)) { int http_status; struct http_resp *resp; /* status: always comes first, we might get more than one response, - * link the previous ones for keepers */ + * discard previous, interim responses */ result = Curl_http_decode_status(&http_status, (const char *)value, valuelen); if(result) @@ -571,7 +588,8 @@ static int proxy_h2_on_header(nghttp2_session *session, result = Curl_http_resp_make(&resp, http_status, NULL); if(result) return NGHTTP2_ERR_CALLBACK_FAILURE; - resp->prev = ctx->tunnel.resp; + if(ctx->tunnel.resp) + Curl_http_resp_free(ctx->tunnel.resp); ctx->tunnel.resp = resp; CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", stream_id, ctx->tunnel.resp->status); @@ -750,15 +768,15 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf, CURLcode result; struct httpreq *req = NULL; - result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2); + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, ctx->dest, + PROXY_HTTP_V2, + (bool)ctx->udp_tunnel); if(result) goto out; result = Curl_creader_set_null(data); if(result) goto out; - infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority); - result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req, NULL, ts, tunnel_send_callback, cf); if(result) { @@ -778,41 +796,30 @@ static CURLcode inspect_response(struct Curl_cfilter *cf, struct Curl_easy *data, struct tunnel_stream *ts) { - CURLcode result = CURLE_OK; - struct dynhds_entry *auth_reply = NULL; - (void)cf; - - DEBUGASSERT(ts->resp); - 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; - } - - if(ts->resp->status == 401) { - auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate"); - } - else if(ts->resp->status == 407) { - auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate"); - } + struct cf_h2_proxy_ctx *ctx = cf->ctx; + proxy_inspect_result res; + CURLcode result; - if(auth_reply) { - CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'", - auth_reply->value); - result = Curl_http_input_auth(data, ts->resp->status == 407, - auth_reply->value); - if(result) - return result; - if(data->req.newurl) { - /* Indicator that we should try again */ - curlx_safefree(data->req.newurl); - h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); - return CURLE_OK; - } + result = Curl_http_proxy_inspect_tunnel_response( + cf, data, ts->resp, (bool)ctx->udp_tunnel, &res); + if(result) + return result; + switch(res) { + case PROXY_INSPECT_OK: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data, + (bool)ctx->udp_tunnel); + break; + case PROXY_INSPECT_FAILED: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); + result = CURLE_COULDNT_CONNECT; + break; + case PROXY_INSPECT_AUTH_RETRY: + h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data, + (bool)ctx->udp_tunnel); + break; } - - /* Seems to have failed */ - return CURLE_COULDNT_CONNECT; + return result; } static CURLcode H2_CONNECT(struct Curl_cfilter *cf, @@ -824,6 +831,11 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, DEBUGASSERT(ts); DEBUGASSERT(ts->authority); + if(ctx->conn_closed) { + failf(data, "proxy closed connection"); + return CURLE_COULDNT_CONNECT; + } + do { switch(ts->state) { case H2_TUNNEL_INIT: @@ -832,7 +844,8 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, result = submit_CONNECT(cf, data, ts); if(result) goto out; - h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data, + (bool)ctx->udp_tunnel); FALLTHROUGH(); case H2_TUNNEL_CONNECT: @@ -841,12 +854,14 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, if(!result) result = proxy_h2_progress_egress(cf, data); if(result && result != CURLE_AGAIN) { - h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); break; } if(ts->has_final_response) { - h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data, + (bool)ctx->udp_tunnel); } else { result = CURLE_OK; @@ -875,7 +890,8 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf, out: if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) - h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data); + h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data, + (bool)ctx->udp_tunnel); return result; } @@ -896,7 +912,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, 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)) + if(tunnel_stream_init(&ctx->tunnel, ctx->dest)) goto out; rc = nghttp2_session_callbacks_new(&cbs); @@ -958,7 +974,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf, out: if(cbs) nghttp2_session_callbacks_del(cbs); - CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result); + CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", (int)result); return result; } @@ -1016,21 +1032,6 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf, return result; } -static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_proxy_ctx *ctx = cf->ctx; - - if(ctx) { - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_h2_proxy_ctx_clear(ctx); - CF_DATA_RESTORE(cf, save); - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void cf_h2_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1133,7 +1134,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d", - want_recv, want_send, result); + want_recv, want_send, (int)result); CF_DATA_RESTORE(cf, save); } else if(ctx->sent_goaway && !cf->shutdown) { @@ -1145,7 +1146,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, want_recv = nghttp2_session_want_read(ctx->h2); result = Curl_pollset_set(data, ps, sock, want_recv, want_send); CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d", - want_recv, want_send, result); + want_recv, want_send, (int)result); CF_DATA_RESTORE(cf, save); } return result; @@ -1194,7 +1195,7 @@ static CURLcode tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %d, %zu", - ctx->tunnel.stream_id, len, result, *pnread); + ctx->tunnel.stream_id, len, (int)result, *pnread); return result; } @@ -1232,14 +1233,15 @@ static CURLcode cf_h2_proxy_recv(struct Curl_cfilter *cf, result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data)); out: - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + if((!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) && (!result || (result == CURLE_AGAIN))) { /* data pending and no fatal error to report. Need to trigger * draining to avoid stalling when no socket events happen. */ drain_tunnel(cf, data, &ctx->tunnel); } CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu", - ctx->tunnel.stream_id, len, result, *pnread); + ctx->tunnel.stream_id, len, (int)result, *pnread); CF_DATA_RESTORE(cf, save); return result; } @@ -1269,7 +1271,8 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", result, *pnwritten); + CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", (int)result, + *pnwritten); if(result && (result != CURLE_AGAIN)) goto out; @@ -1298,7 +1301,8 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } out: - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && + if((!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) || + !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) && (!result || (result == CURLE_AGAIN))) { /* data pending and no fatal error to report. Need to trigger * draining to avoid stalling when no socket events happen. */ @@ -1306,7 +1310,7 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, } CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, " "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", - ctx->tunnel.stream_id, len, result, *pnwritten, + ctx->tunnel.stream_id, len, (int)result, *pnwritten, nghttp2_session_get_stream_remote_window_size( ctx->h2, ctx->tunnel.stream_id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -1338,7 +1342,7 @@ static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "[%d] flush -> %d, " "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)", - ctx->tunnel.stream_id, result, + ctx->tunnel.stream_id, (int)result, nghttp2_session_get_stream_remote_window_size( ctx->h2, ctx->tunnel.stream_id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -1410,8 +1414,8 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_HOST_PORT: - *pres1 = (int)cf->conn->http_proxy.port; - *((const char **)pres2) = cf->conn->http_proxy.host.name; + *pres1 = (int)ctx->dest->port; + *((const char **)pres2) = ctx->dest->hostname; return CURLE_OK; case CF_QUERY_NEED_FLUSH: { if(!Curl_bufq_is_empty(&ctx->outbufq) || @@ -1464,7 +1468,6 @@ struct Curl_cftype Curl_cft_h2_proxy = { CURL_LOG_LVL_NONE, cf_h2_proxy_destroy, cf_h2_proxy_connect, - cf_h2_proxy_close, cf_h2_proxy_shutdown, cf_h2_proxy_adjust_pollset, cf_h2_proxy_data_pending, @@ -1477,7 +1480,9 @@ struct Curl_cftype Curl_cft_h2_proxy = { }; CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel) { struct Curl_cfilter *cf_h2_proxy = NULL; struct cf_h2_proxy_ctx *ctx; @@ -1487,17 +1492,17 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) goto out; + Curl_peer_link(&ctx->dest, dest); + ctx->udp_tunnel = udp_tunnel; result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx); if(result) goto out; - + ctx = NULL; Curl_conn_cf_insert_after(cf, cf_h2_proxy); - result = CURLE_OK; out: - if(result) - cf_h2_proxy_ctx_free(ctx); + cf_h2_proxy_ctx_free(ctx); return result; } diff --git a/lib/cf-h2-proxy.h b/lib/cf-h2-proxy.h index 318ce1973fa1..07e3c9aedf1a 100644 --- a/lib/cf-h2-proxy.h +++ b/lib/cf-h2-proxy.h @@ -28,7 +28,9 @@ #if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *dest, + bool udp_tunnel); extern struct Curl_cftype Curl_cft_h2_proxy; diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 9ee5e790ebb5..16ee25066c0e 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -28,6 +28,7 @@ #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" +#include "connect.h" #include "curl_addrinfo.h" #include "curl_trc.h" #include "select.h" @@ -44,13 +45,6 @@ struct cf_haproxy_ctx { struct dynbuf data_out; }; -static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx) -{ - DEBUGASSERT(ctx); - ctx->state = HAPROXY_INIT; - curlx_dyn_reset(&ctx->data_out); -} - static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx) { if(ctx) { @@ -78,7 +72,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, DEBUGASSERT(ctx); DEBUGASSERT(ctx->state == HAPROXY_INIT); #ifdef USE_UNIX_SOCKETS - if(cf->conn->unix_domain_socket) + if(Curl_conn_get_first_peer(cf->conn, cf->sockindex)->unix_socket) /* the buffer is large enough to hold this! */ result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n")); else { @@ -97,7 +91,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, client_dest_ip = ipquad.remote_ip; } - result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", + result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %d %d\r\n", is_ipv6 ? "TCP6" : "TCP4", client_source_ip, client_dest_ip, ipquad.local_port, ipquad.remote_port); @@ -172,16 +166,6 @@ static void cf_haproxy_destroy(struct Curl_cfilter *cf, cf_haproxy_ctx_free(cf->ctx); } -static void cf_haproxy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - cf_haproxy_ctx_reset(cf->ctx); - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps) @@ -201,7 +185,6 @@ struct Curl_cftype Curl_cft_haproxy = { 0, cf_haproxy_destroy, cf_haproxy_connect, - cf_haproxy_close, Curl_cf_def_shutdown, cf_haproxy_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index d1d8f51076e3..1a2e966ef66a 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -29,6 +29,7 @@ #include "curl_trc.h" #include "cfilters.h" #include "cf-dns.h" +#include "cf-setup.h" #include "connect.h" #include "hostip.h" #include "httpsrr.h" @@ -62,7 +63,6 @@ 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; } @@ -304,13 +304,14 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, size_t i; /* Do we have HTTPS-RR information? */ - rr = Curl_conn_dns_get_https(data, cf->sockindex); + rr = Curl_conn_dns_get_https( + data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); /* We do not support `rr->no_def_alpn`. */ if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) { 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 */ + if(alpn_rr == not_this_one) /* do not want this one */ continue; switch(alpn_rr) { case ALPN_h3: @@ -493,7 +494,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->httpsrr_resolved) { - ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex); + ctx->httpsrr_resolved = Curl_conn_dns_resolved_https( + data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex)); #ifdef DEBUGBUILD if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) { CURL_TRC_CF(data, cf, "awaiting HTTPS-RR"); @@ -576,7 +578,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } out: - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", (int)result, *done); return result; } @@ -616,7 +618,7 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, result = ctx->ballers[i].result; } } - CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done); return result; } @@ -635,7 +637,8 @@ static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf, continue; result = Curl_conn_cf_adjust_pollset(b->cf, data, ps); } - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result, + ps->n); } return result; } @@ -731,18 +734,6 @@ static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf, return result; } -static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "close"); - cf_hc_ctx_close(data, cf->ctx); - cf->connected = FALSE; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; @@ -757,7 +748,6 @@ struct Curl_cftype Curl_cft_http_connect = { CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, - cf_hc_close, cf_hc_shutdown, cf_hc_adjust_pollset, cf_hc_data_pending, @@ -822,9 +812,14 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS); + /* This filter is intended for HTTPS using ALPN and does + * not support HTTPS Eyeballing to a proxy. */ if((conn->scheme->protocol != CURLPROTO_HTTPS) || +#ifndef CURL_DISABLE_PROXY + conn->bits.origin_is_proxy || +#endif !conn->bits.tls_enable_alpn) - goto out; + goto out; result = cf_hc_add(data, conn, sockindex, conn->transport_wanted); diff --git a/lib/cf-ip-happy.c b/lib/cf-ip-happy.c index f67273e48963..963ccea94d78 100644 --- a/lib/cf-ip-happy.c +++ b/lib/cf-ip-happy.c @@ -56,12 +56,14 @@ #include "multiif.h" #include "progress.h" #include "select.h" +#include "sockaddr.h" #include "vquic/vquic.h" /* for quic cfilters */ struct transport_provider { - uint8_t transport; cf_ip_connect_create *cf_create; + uint8_t transport; + bool tunnel; }; static @@ -69,23 +71,30 @@ static const #endif struct transport_provider transport_providers[] = { - { TRNSPRT_TCP, Curl_cf_tcp_create }, + { Curl_cf_tcp_create, TRNSPRT_TCP, FALSE }, + { Curl_cf_tcp_create, TRNSPRT_TCP, TRUE }, #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) - { TRNSPRT_QUIC, Curl_cf_quic_create }, + { Curl_cf_quic_create, TRNSPRT_QUIC, FALSE }, +#endif +#if !defined(CURL_DISABLE_HTTP) && defined(USE_PROXY_HTTP3) + { Curl_cf_h3_proxy_create, TRNSPRT_QUIC, TRUE }, #endif #ifndef CURL_DISABLE_TFTP - { TRNSPRT_UDP, Curl_cf_udp_create }, + { Curl_cf_udp_create, TRNSPRT_UDP, FALSE }, #endif #ifdef USE_UNIX_SOCKETS - { TRNSPRT_UNIX, Curl_cf_unix_create }, + { Curl_cf_unix_create, TRNSPRT_UNIX, FALSE }, + { Curl_cf_unix_create, TRNSPRT_UNIX, TRUE }, #endif }; -static cf_ip_connect_create *get_cf_create(uint8_t transport) +static cf_ip_connect_create *get_cf_create(uint8_t transport, + bool tunnel) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { - if(transport == transport_providers[i].transport) + if((transport == transport_providers[i].transport) && + (tunnel == transport_providers[i].tunnel)) return transport_providers[i].cf_create; } return NULL; @@ -102,7 +111,6 @@ UNITTEST void debug_set_transport_provider( for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { if(transport == transport_providers[i].transport) { transport_providers[i].cf_create = cf_create; - return; } } } @@ -110,15 +118,18 @@ UNITTEST void debug_set_transport_provider( struct cf_ai_iter { struct Curl_cfilter *cf; + struct Curl_peer *peer; int ai_family; unsigned int n; }; static void cf_ai_iter_init(struct cf_ai_iter *iter, struct Curl_cfilter *cf, + struct Curl_peer *peer, int ai_family) { iter->cf = cf; + iter->peer = peer; /* not linked, ctx->ballers owns and has same lifetime */ iter->ai_family = ai_family; iter->n = 0; } @@ -131,7 +142,7 @@ static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter, if(!iter->cf) return NULL; - addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex, + addr = Curl_conn_dns_get_ai(data, iter->peer, iter->cf->sockindex, iter->ai_family, iter->n); if(addr) iter->n++; @@ -142,19 +153,23 @@ static bool cf_ai_iter_has_more(struct cf_ai_iter *iter, struct Curl_easy *data) { return (iter->cf && - !!Curl_conn_dns_get_ai(data, iter->cf->sockindex, + !!Curl_conn_dns_get_ai(data, iter->peer, iter->cf->sockindex, iter->ai_family, iter->n)); } struct cf_ip_attempt { struct cf_ip_attempt *next; + struct Curl_peer *origin; + struct Curl_peer *peer; + struct Curl_peer *tunnel_peer; 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; - uint8_t transport; + uint8_t transport_peer; + uint8_t tunnel_transport; int error; BIT(connected); /* cf has connected */ BIT(shutdown); /* cf has shutdown */ @@ -168,16 +183,23 @@ static void cf_ip_attempt_free(struct cf_ip_attempt *a, if(a) { if(a->cf) Curl_conn_cf_discard_chain(&a->cf, data); + Curl_peer_unlink(&a->origin); + Curl_peer_unlink(&a->peer); + Curl_peer_unlink(&a->tunnel_peer); curlx_free(a); } } static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, - struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_cfilter *cf, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct Curl_sockaddr_ex *addr, int ai_family, - uint8_t transport, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, cf_ip_connect_create *cf_create) { struct Curl_cfilter *wcf; @@ -189,14 +211,20 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, if(!a) return CURLE_OUT_OF_MEMORY; + Curl_peer_link(&a->origin, origin); + Curl_peer_link(&a->peer, peer); + a->transport_peer = transport_peer; + Curl_peer_link(&a->tunnel_peer, tunnel_peer); + a->tunnel_transport = tunnel_transport; 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, a->transport); + result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer, + cf->conn, &a->addr, a->tunnel_peer, + a->tunnel_transport); if(result) goto out; @@ -245,13 +273,17 @@ struct cf_ip_ballers { #ifdef USE_IPV6 struct cf_ai_iter ipv6_iter; #endif + struct Curl_peer *origin; + struct Curl_peer *peer; + struct Curl_peer *tunnel_peer; cf_ip_connect_create *cf_create; /* for creating cf */ struct curltime started; struct curltime last_attempt_started; timediff_t attempt_delay_ms; int last_attempt_ai_family; uint32_t max_concurrent; - uint8_t transport; + uint8_t transport_peer; + uint8_t tunnel_transport; }; static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, @@ -269,7 +301,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, 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, a->origin, a->peer, a->transport_peer, + cf->conn, &a->addr, + a->tunnel_peer, a->tunnel_transport); if(!result) { bool dummy; /* the new filter might have sub-filters */ @@ -282,11 +316,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a, return result; } -static void cf_ip_ballers_clear(struct Curl_cfilter *cf, - struct Curl_easy *data, +static void cf_ip_ballers_clear(struct Curl_easy *data, struct cf_ip_ballers *bs) { - (void)cf; while(bs->running) { struct cf_ip_attempt *a = bs->running; bs->running = a->next; @@ -294,35 +326,36 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf, } cf_ip_attempt_free(bs->winner, data); bs->winner = NULL; + Curl_peer_unlink(&bs->origin); + Curl_peer_unlink(&bs->peer); + Curl_peer_unlink(&bs->tunnel_peer); } static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, - struct Curl_cfilter *cf, - cf_ip_connect_create *cf_create, - uint8_t transport, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, timediff_t attempt_delay_ms, uint32_t max_concurrent) { memset(bs, 0, sizeof(*bs)); - bs->cf_create = cf_create; - bs->transport = transport; + bs->cf_create = get_cf_create(transport_peer, !!tunnel_peer); + if(!bs->cf_create) { + failf(data, "unsupported transport type %u%s", + transport_peer, tunnel_peer ? " to proxy" : ""); + return CURLE_UNSUPPORTED_PROTOCOL; + } + Curl_peer_link(&bs->origin, origin); + Curl_peer_link(&bs->peer, peer); + bs->transport_peer = transport_peer; + Curl_peer_link(&bs->tunnel_peer, tunnel_peer); + bs->tunnel_transport = tunnel_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, cf, AF_UNIX); -#else - return CURLE_UNSUPPORTED_PROTOCOL; -#endif - } - else { /* TCP/UDP/QUIC */ -#ifdef USE_IPV6 - cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6); -#endif - cf_ai_iter_init(&bs->addr_iter, cf, AF_INET); - } return CURLE_OK; } @@ -458,15 +491,17 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, 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); + result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_peer); if(result) goto out; - result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family, - bs->transport, bs->cf_create); + result = cf_ip_attempt_new(&a, data, cf, bs->origin, bs->peer, + bs->transport_peer, &addr, ai_family, + bs->tunnel_peer, bs->tunnel_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); + (ai_family == AF_INET) ? "4" : "6", (int)result); if(result) goto out; DEBUGASSERT(a); @@ -495,7 +530,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, if(!a->inconclusive) continue; result = cf_ip_attempt_restart(a, cf, data); - CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result); + CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, (int)result); if(result) /* serious failure */ goto out; bs->last_attempt_started = *Curl_pgrs_now(data); @@ -518,7 +553,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, result = CURLE_COULDNT_CONNECT; VERBOSE(i = 0); for(a = bs->running; a; a = a->next) { - CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result); + CURL_TRC_CF(data, cf, "baller %d: result=%d", i, (int)a->result); if(a->result) result = a->result; } @@ -652,7 +687,6 @@ typedef enum { } cf_connect_state; struct cf_ip_happy_ctx { - uint8_t transport; cf_ip_connect_create *cf_create; cf_connect_state state; struct cf_ip_ballers ballers; @@ -674,40 +708,39 @@ static CURLcode is_connected(struct Curl_cfilter *cf, if(!result) return CURLE_OK; else { - const char *hostname, *proxy_name = NULL; + struct Curl_peer *peer = NULL, *proxy_peer = NULL; char viamsg[160]; + + peer = Curl_conn_get_first_peer(conn, cf->sockindex); + if(!conn->origin || !peer) + return CURLE_FAILED_INIT; + #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; + if(conn->socks_proxy.peer) + proxy_peer = conn->socks_proxy.peer; + else if(conn->http_proxy.peer) + proxy_peer = conn->http_proxy.peer; #endif - hostname = conn->bits.conn_to_host ? conn->conn_to_host.name : - conn->host.name; + viamsg[0] = 0; + if(!Curl_peer_equal(peer, conn->origin) && + !Curl_peer_equal(peer, proxy_peer)) { #ifdef USE_UNIX_SOCKETS - if(conn->unix_domain_socket) - curl_msnprintf(viamsg, sizeof(viamsg), "over %s", - conn->unix_domain_socket); - else -#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; + if(peer->unix_socket) + curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s", + peer->hostname); else - port = conn->remote_port; - curl_msnprintf(viamsg, sizeof(viamsg), "port %d", port); +#endif + curl_msnprintf(viamsg, sizeof(viamsg), " via %s:%u", + peer->hostname, peer->port); } - failf(data, "Failed to connect to %s %s %s%s%safter " + failf(data, "Failed to connect to %s:%u%s %s%s%safter " "%" FMT_TIMEDIFF_T " ms: %s", - hostname, viamsg, - proxy_name ? "via " : "", - proxy_name ? proxy_name : "", - proxy_name ? " " : "", + conn->origin->hostname, conn->origin->port, viamsg, + proxy_peer ? "over proxy " : "", + proxy_peer ? proxy_peer->hostname : "", + proxy_peer ? " " : "", curlx_ptimediff_ms(Curl_pgrs_now(data), &data->progress.t_startsingle), curl_easy_strerror(result)); @@ -734,28 +767,41 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT; } - CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport); + if(ctx->ballers.transport_peer == TRNSPRT_UNIX) { +#ifdef USE_UNIX_SOCKETS + cf_ai_iter_init(&ctx->ballers.addr_iter, cf, ctx->ballers.peer, AF_UNIX); +#else + return CURLE_UNSUPPORTED_PROTOCOL; +#endif + } + else { /* TCP/UDP/QUIC */ +#ifdef USE_IPV6 + cf_ai_iter_init(&ctx->ballers.ipv6_iter, cf, ctx->ballers.peer, AF_INET6); +#endif + cf_ai_iter_init(&ctx->ballers.addr_iter, cf, ctx->ballers.peer, AF_INET); + } + + CURL_TRC_CF(data, cf, "init ip ballers for transport %u", + ctx->ballers.transport_peer); 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); + return CURLE_OK; } -static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf, +static void cf_ip_happy_ctx_clear(struct cf_ip_happy_ctx *ctx, struct Curl_easy *data) { - struct cf_ip_happy_ctx *ctx = cf->ctx; - DEBUGASSERT(ctx); - DEBUGASSERT(data); - cf_ip_ballers_clear(cf, data, &ctx->ballers); + if(ctx) + cf_ip_ballers_clear(data, &ctx->ballers); } -static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx) +static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx, + struct Curl_easy *data) { - if(ctx) + if(ctx) { + cf_ip_happy_ctx_clear(ctx, data); curlx_free(ctx); + } } static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, @@ -772,7 +818,7 @@ static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, } result = cf_ip_ballers_shutdown(&ctx->ballers, data, done); - CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done); return result; } @@ -785,7 +831,8 @@ static CURLcode cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf, if(!cf->connected) { result = cf_ip_ballers_pollset(&ctx->ballers, data, ps); - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result, + ps->n); } return result; } @@ -810,7 +857,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, *done = FALSE; if(!ctx->dns_resolved) { - result = Curl_conn_dns_result(cf->conn, cf->sockindex); + result = Curl_conn_dns_result(cf->conn, cf->sockindex, ctx->ballers.peer); if(!result) ctx->dns_resolved = TRUE; else if(result == CURLE_AGAIN) { @@ -841,9 +888,9 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, cf->connected = TRUE; cf->next = ctx->ballers.winner->cf; ctx->ballers.winner->cf = NULL; - cf_ip_happy_ctx_clear(cf, data); + cf_ip_happy_ctx_clear(ctx, data); Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS); - /* whatever errors where reported by ballers, clear our errorbuf */ + /* whatever errors were reported by ballers, clear our errorbuf */ Curl_reset_fail(data); if(cf->conn->scheme->protocol & PROTO_FAMILY_SSH) @@ -871,22 +918,6 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf, return result; } -static void cf_ip_happy_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ip_happy_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "close"); - cf_ip_happy_ctx_clear(cf, data); - cf->connected = FALSE; - ctx->state = SCFST_INIT; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - static bool cf_ip_happy_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -940,8 +971,8 @@ 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); + cf_ip_happy_ctx_clear(ctx, data); + cf_ip_happy_ctx_destroy(ctx, data); } } @@ -951,7 +982,6 @@ struct Curl_cftype Curl_cft_ip_happy = { CURL_LOG_LVL_NONE, cf_ip_happy_destroy, cf_ip_happy_connect, - cf_ip_happy_close, cf_ip_happy_shutdown, cf_ip_happy_adjust_pollset, cf_ip_happy_data_pending, @@ -963,21 +993,14 @@ struct Curl_cftype Curl_cft_ip_happy = { cf_ip_happy_query, }; -/** - * Create an IP happy eyeball connection filter that uses the, once resolved, - * address information to connect on ip families based on connection - * configuration. - * @param pcf output, the created cfilter - * @param data easy handle used in creation - * @param conn connection the filter is created for - * @param cf_create method to create the sub-filters performing the - * actual connects. - */ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, - cf_ip_connect_create *cf_create, - uint8_t transport) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_ip_happy_ctx *ctx = NULL; CURLcode result; @@ -990,35 +1013,39 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - ctx->transport = transport; - ctx->cf_create = cf_create; + result = cf_ip_ballers_init(&ctx->ballers, data, + origin, peer, transport_peer, + tunnel_peer, tunnel_transport, + data->set.happy_eyeballs_timeout, + IP_HE_MAX_CONCURRENT_ATTEMPTS); + if(result) + goto out; result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx); out: if(result) { curlx_safefree(*pcf); - cf_ip_happy_ctx_destroy(ctx); + cf_ip_happy_ctx_destroy(ctx, data); } return result; } CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - uint8_t transport) + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { - cf_ip_connect_create *cf_create; struct Curl_cfilter *cf; CURLcode result; /* Need to be first */ DEBUGASSERT(cf_at); - cf_create = get_cf_create(transport); - if(!cf_create) { - 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); + result = cf_ip_happy_create(&cf, data, origin, peer, transport_peer, + cf_at->conn, tunnel_peer, tunnel_transport); if(result) return result; diff --git a/lib/cf-ip-happy.h b/lib/cf-ip-happy.h index 547ee4b4ac90..dd9b29ac8f6a 100644 --- a/lib/cf-ip-happy.h +++ b/lib/cf-ip-happy.h @@ -29,28 +29,44 @@ struct connectdata; struct Curl_addrinfo; struct Curl_cfilter; struct Curl_easy; +struct Curl_peer; struct Curl_sockaddr_ex; /** - * Create a cfilter for making an "ip" connection to the - * given address, using parameters from `conn`. The "ip" connection - * can be a TCP socket, a UDP socket or even a QUIC connection. + * Create a cfilter to connect to `origin` via an optional `peer` + * using `transport_peer` and `addr`. + * With a `tunnel_peer` present, the filter will be used to proxy tunnel + * to it and the tunnel will use `tunnel_transport`. + * `pcf`: the filter created on success + * `data`: the transfer initiating the connect + * `conn`: the connection that gets connected * - * It MUST use only the supplied `ai` for its connection attempt. - * - * Such a filter may be used in "happy eyeball" scenarios, and its - * `connect` implementation needs to support non-blocking. Once connected, + * The filter is used in "happy eyeball" scenarios. Once connected, * it MAY be installed in the connection filter chain to serve transfers. */ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); +/** + * Create an IP happy eyeball connection filter that connects to `origin` + * via an optional `peer` using `transport_peer`. + * With a `tunnel_peer` present, the filter will be used to proxy tunnel + * to it and the tunnel will use `tunnel_transport`. + */ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - uint8_t transport); + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); extern struct Curl_cftype Curl_cft_ip_happy; diff --git a/lib/cf-recvbuf.c b/lib/cf-recvbuf.c new file mode 100644 index 000000000000..8ccc148bd3d9 --- /dev/null +++ b/lib/cf-recvbuf.c @@ -0,0 +1,157 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 CURL_DISABLE_WEBSOCKETS +/* only used for this protocol, so far */ + +#include "urldata.h" +#include "bufq.h" +#include "cfilters.h" +#include "cf-recvbuf.h" +#include "curl_trc.h" + +#define CURL_CF_RECVBUF_CHUNK (16 * 1024) + +struct cf_recvbuf_ctx { + struct bufq recvbuf; +}; + +static void cf_recvbuf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + (void)data; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + curlx_free(ctx); + } +} + +static CURLcode cf_recvbuf_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + size_t *pnread) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + + if(!Curl_bufq_is_empty(&ctx->recvbuf)) { + return Curl_bufq_cread(&ctx->recvbuf, buf, len, pnread); + } + + if(cf->next) + return cf->next->cft->do_recv(cf->next, data, buf, len, pnread); + *pnread = 0; + return CURLE_RECV_ERROR; +} + +static bool cf_recvbuf_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_recvbuf_ctx *ctx = cf->ctx; + + if(!Curl_bufq_is_empty(&ctx->recvbuf)) + return TRUE; + + return cf->next ? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +struct Curl_cftype Curl_cft_recvbuf = { + "RECVBUF", + 0, + CURL_LOG_LVL_NONE, + cf_recvbuf_destroy, + Curl_cf_def_connect, + Curl_cf_def_shutdown, + Curl_cf_def_adjust_pollset, + cf_recvbuf_data_pending, + Curl_cf_def_send, + cf_recvbuf_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_recvbuf_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + const uint8_t *buf, size_t blen) +{ + struct Curl_cfilter *cf = NULL; + struct cf_recvbuf_ctx *ctx; + CURLcode result = CURLE_OK; + size_t nwritten = 0; + + (void)data; + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_bufq_init2(&ctx->recvbuf, CURL_CF_RECVBUF_CHUNK, + (blen / CURL_CF_RECVBUF_CHUNK) + 1, + (BUFQ_OPT_SOFT_LIMIT | BUFQ_OPT_NO_SPARES)); + result = Curl_bufq_write(&ctx->recvbuf, buf, blen, &nwritten); + if(result) + goto out; + if(nwritten != blen) { + result = CURLE_FAILED_INIT; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_recvbuf, ctx); + if(result) + goto out; + ctx = NULL; + +out: + *pcf = result ? NULL : cf; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + curlx_free(ctx); + } + return result; +} + +CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const uint8_t *buf, size_t blen) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_recvbuf_create(&cf, data, buf, blen); + if(result) + goto out; + + cf->connected = Curl_conn_is_connected(conn, sockindex); + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +#endif /* !CURL_DISABLE_WEBSOCKETS */ diff --git a/lib/cf-recvbuf.h b/lib/cf-recvbuf.h new file mode 100644 index 000000000000..1b08fcc3be66 --- /dev/null +++ b/lib/cf-recvbuf.h @@ -0,0 +1,40 @@ +#ifndef HEADER_CURL_CF_RECVBUF_H +#define HEADER_CURL_CF_RECVBUF_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" + +#ifndef CURL_DISABLE_WEBSOCKETS +/* only used for this protocol, so far */ + +CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + const uint8_t *buf, size_t blen); + +extern struct Curl_cftype Curl_cft_recvbuf; + +#endif /* !CURL_DISABLE_WEBSOCKETS */ + +#endif /* HEADER_CURL_CF_RECVBUF_H */ diff --git a/lib/cf-setup.c b/lib/cf-setup.c new file mode 100644 index 000000000000..119294a89a4d --- /dev/null +++ b/lib/cf-setup.c @@ -0,0 +1,476 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "cfilters.h" +#include "cf-haproxy.h" +#include "cf-ip-happy.h" +#include "cf-setup.h" +#include "curl_trc.h" +#include "connect.h" +#include "http_proxy.h" +#include "socks.h" +#include "vquic/cf-capsule.h" +#include "vquic/vquic.h" +#include "vtls/vtls.h" + + +typedef enum { + CF_SETUP_INIT, + CF_SETUP_CNNCT_EYEBALLS, + CF_SETUP_CNNCT_SOCKS, + CF_SETUP_CNNCT_HTTP_PROXY, + CF_SETUP_CNNCT_HAPROXY, + CF_SETUP_CNNCT_SSL, + CF_SETUP_DONE +} cf_setup_state; + +struct cf_setup_ctx { + cf_setup_state state; + int ssl_mode; + uint8_t transport; + uint8_t retry_count; +}; + +#ifndef CURL_DISABLE_PROXY + +static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { + if(data->set.haproxyprotocol) { + if(ctx->transport == TRNSPRT_QUIC) { + failf(data, "haproxy protocol does not support QUIC"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + result = Curl_cf_haproxy_insert_after(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d", + (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added HAPROXY filter"); + } + ctx->state = CF_SETUP_CNNCT_HAPROXY; + } + return result; +} + +static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) { + /* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/ + struct Curl_peer *second_peer; + + if(cf->conn->http_proxy.peer) + second_peer = cf->conn->http_proxy.peer; + else + second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); + if(!second_peer) + return CURLE_FAILED_INIT; + + result = Curl_cf_socks_proxy_insert_after( + cf, data, second_peer, cf->conn->ip_version, + cf->conn->socks_proxy.proxytype, + cf->conn->socks_proxy.creds); + if(result) { + CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result); + return result; + } + + CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u", + second_peer->hostname, second_peer->port); + ctx->state = CF_SETUP_CNNCT_SOCKS; + } + return result; +} + +#ifndef CURL_DISABLE_HTTP +static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && + cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { + struct Curl_peer *peer = cf->conn->http_proxy.peer; + struct Curl_peer *tunnel_peer = + Curl_conn_get_destination(cf->conn, cf->sockindex); + +#ifdef USE_SSL + if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) && + !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { + result = Curl_cf_ssl_proxy_insert_after( + cf, data, cf->conn->http_proxy.peer); + if(result) { + CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d", + (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy"); + } +#endif /* USE_SSL */ + + result = Curl_cf_http_proxy_insert_after( + cf, data, peer, tunnel_peer, + ctx->transport, cf->conn->http_proxy.proxytype); + if(result) { + CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d", + (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); + ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; + } + return result; +} +#endif /* !CURL_DISABLE_HTTP */ +#endif /* CURL_DISABLE_PROXY */ + +/* Get the origin curl connects its socket to. + * Can be origin or the first proxy. */ +static struct Curl_peer *conn_get_first_origin(struct connectdata *conn, + int sockindex) +{ +#ifndef CURL_DISABLE_PROXY + if(conn->socks_proxy.peer) + return conn->socks_proxy.peer; + if(conn->http_proxy.peer) + return conn->http_proxy.peer; +#endif + return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin; +} + +static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { + /* What is the first hop we directly connect to and what transport + * do we use for it? Only on the first hop we can do Happy Eyeballs. + * first_origin and first_peer differ on --connect-to. */ + struct Curl_peer *first_origin = + conn_get_first_origin(cf->conn, cf->sockindex); + struct Curl_peer *first_peer = + Curl_conn_get_first_peer(cf->conn, cf->sockindex); + struct Curl_peer *tunnel_peer = NULL; + uint8_t first_transport = ctx->transport; + + if(!first_peer) + return CURLE_FAILED_INIT; + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { + first_transport = + Curl_http_proxy_transport(cf->conn->http_proxy.proxytype); + tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); + if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) { + failf(data, "HTTP/3 proxy not possible via SOCKS"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + } +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + + result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer, + first_transport, + tunnel_peer, ctx->transport); + if(result) { + CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result); + return result; + } + + if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) { + CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u", + first_peer->hostname, first_peer->port); + ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; + } + else { + CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u", + tunnel_peer ? "proxy" : "origin", + first_peer->hostname, first_peer->port); + ctx->state = CF_SETUP_CNNCT_EYEBALLS; + } + } + return result; +} + +static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)data; /* not used in all builds */ + if(ctx->state < CF_SETUP_CNNCT_SSL) { +#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ + !defined(CURL_DISABLE_PROXY) + + /* Wanting QUIC with an HTTP tunneling filter, we now need to add + * the QUIC filter on top. Without tunneling, this has already + * happened in the Happy Eyeball filter. */ + if(ctx->transport == TRNSPRT_QUIC && + cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { + struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex); + struct Curl_peer *peer = + Curl_conn_get_destination(cf->conn, cf->sockindex); + + result = Curl_cf_capsule_insert_after(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d", + (int)result); + return result; + } + result = Curl_cf_quic_insert_after(cf, origin, peer); + if(result) { + CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added QUIC filter for origin"); + } + else +#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */ +#ifdef USE_SSL + 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 */ + +#ifndef CURL_DISABLE_PROXY + if(cf->conn->bits.origin_is_proxy) { + result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin); + } + else +#endif + { + /* Another FTP quirk: when adding SSL verification, to a DATA + * connection, always verify against the control's origin */ + struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET); + struct Curl_peer *peer = + Curl_conn_get_destination(cf->conn, cf->sockindex); + result = Curl_cf_ssl_insert_after(cf, data, origin, peer); + } + if(result) { + CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d", + (int)result); + return result; + } + CURL_TRC_CF(data, cf, "added SSL filter for origin"); + } +#endif /* USE_SSL */ + ctx->state = CF_SETUP_CNNCT_SSL; + } + return result; +} + +static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* connect current sub-chain */ +connect_sub_chain: + VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect")); + + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + result = cf_setup_add_ip_happy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + +#ifndef CURL_DISABLE_PROXY + result = cf_setup_add_socks(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + +#ifndef CURL_DISABLE_HTTP + result = cf_setup_add_http_proxy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; +#endif /* !CURL_DISABLE_HTTP */ + + result = cf_setup_add_haproxy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; +#endif /* !CURL_DISABLE_PROXY */ + + result = cf_setup_add_origin_filters(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + + ctx->state = CF_SETUP_DONE; + cf->connected = TRUE; + *done = TRUE; + return CURLE_OK; +} + +static CURLcode cf_setup_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result; + + /* In some situations, a server/proxy may close the connection and + * we need to connect again (HTTP/1.x proxy auth, for example). + * We used to close the filters and reuse them for another attempt, + * however that complicates filter code and it is simpler to tear them + * all down and start over. */ +retry: + result = cf_setup_connect_steps(cf, data, done); + + if(result == CURLE_AGAIN) { + ++ctx->retry_count; + if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */ + return CURLE_COULDNT_CONNECT; + + CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count); + Curl_conn_cf_discard_chain(&cf->next, data); + ctx->state = CF_SETUP_INIT; + goto retry; + } + return result; +} + +static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "destroy"); + curlx_safefree(ctx); +} + +struct Curl_cftype Curl_cft_setup = { + "SETUP", + CF_TYPE_SETUP, + CURL_LOG_LVL_NONE, + cf_setup_destroy, + cf_setup_connect, + Curl_cf_def_shutdown, + Curl_cf_def_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + Curl_cf_def_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_setup_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + uint8_t transport, + int ssl_mode) +{ + struct Curl_cfilter *cf = NULL; + struct cf_setup_ctx *ctx; + CURLcode result = CURLE_OK; + + (void)data; + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + ctx->state = CF_SETUP_INIT; + ctx->ssl_mode = ssl_mode; + ctx->transport = transport; + + result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); + if(result) + goto out; + ctx = NULL; + +out: + *pcf = result ? NULL : cf; + if(ctx) { + curlx_free(ctx); + } + return result; +} + +CURLcode Curl_cf_setup_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t transport, + int ssl_mode) +{ + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + result = cf_setup_create(&cf, data, transport, ssl_mode); + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t transport, + int ssl_mode) +{ + struct Curl_cfilter *cf; + CURLcode result; + + DEBUGASSERT(data); + result = cf_setup_create(&cf, data, transport, ssl_mode); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); +out: + return result; +} diff --git a/lib/cf-setup.h b/lib/cf-setup.h new file mode 100644 index 000000000000..80e20064b80a --- /dev/null +++ b/lib/cf-setup.h @@ -0,0 +1,46 @@ +#ifndef HEADER_CURL_CF_SETUP_H +#define HEADER_CURL_CF_SETUP_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_dns_entry; +struct ip_quadruple; +struct Curl_peer; +struct Curl_str; + +CURLcode Curl_cf_setup_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t transport, + int ssl_mode); + +CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t transport, + int ssl_mode); + +extern struct Curl_cftype Curl_cft_setup; + +#endif /* HEADER_CURL_CF_SETUP_H */ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index b99bcdef5518..0741f2ba415c 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -26,6 +26,9 @@ #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,16 +47,17 @@ #include #endif +#ifdef HAVE_IFADDRS_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif #ifdef __VMS #include #include #endif -#ifdef __DragonFly__ -/* Required for __DragonFly_version */ -#include -#endif - #include "urldata.h" #include "curl_trc.h" #include "if2ip.h" @@ -63,11 +67,13 @@ #include "curl_addrinfo.h" #include "select.h" #include "multiif.h" +#include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "progress.h" #include "conncache.h" #include "multihandle.h" #include "rand.h" +#include "sockaddr.h" #include "curlx/strdup.h" #include "system_win32.h" #include "curlx/nonblock.h" @@ -77,6 +83,63 @@ #include "curlx/strparse.h" +/* retrieves ip address and port from a sockaddr structure. note it calls + * curlx_inet_ntop which sets errno on fail, not SOCKERRNO. + * @unittest 1607 + */ +UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, uint16_t *port); +UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen, + char *addr, uint16_t *port) +{ + struct sockaddr_in *si = NULL; +#ifdef USE_IPV6 + struct sockaddr_in6 *si6 = NULL; +#endif +#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)) { + *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 +#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; + errno = SOCKEAFNOSUPPORT; + return FALSE; +} + static void tcpnodelay(struct Curl_cfilter *cf, struct Curl_easy *data, curl_socket_t sockfd) @@ -86,8 +149,7 @@ static void tcpnodelay(struct Curl_cfilter *cf, int level = IPPROTO_TCP; VERBOSE(char buffer[STRERROR_LEN]); - if(setsockopt(sockfd, level, TCP_NODELAY, - (void *)&onoff, sizeof(onoff)) < 0) + if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0) CURL_TRC_CF(data, cf, "Could not set TCP_NODELAY: %s", curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); #else @@ -97,8 +159,8 @@ static void tcpnodelay(struct Curl_cfilter *cf, #endif } -#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \ - defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \ +#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)) || \ @@ -132,14 +194,15 @@ static void tcpkeepalive(struct Curl_cfilter *cf, 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+. */ +/* Offered by mingw-w64 v12+, MS SDK 6.0A/VS2008+ */ #ifndef TCP_KEEPALIVE #define TCP_KEEPALIVE 3 #endif -/* Offered by mingw-w64 v12+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v12+, MS SDK 10.0.15063.0/VS2017 15.1+ */ #ifndef TCP_KEEPCNT #define TCP_KEEPCNT 16 #endif +/* Offered by mingw-w64 v12+, MS SDK 10.0.16299.0/VS2017 15.4+ */ #ifndef TCP_KEEPIDLE #define TCP_KEEPIDLE TCP_KEEPALIVE #endif @@ -302,6 +365,49 @@ int Curl_sock_nosigpipe(curl_socket_t sockfd) } #endif /* USE_SO_NOSIGPIPE */ +#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) +static uint32_t get_scope_id(struct Curl_easy *data, + struct sockaddr_in6 *sa6) +{ + uint32_t scope_id = 0; + if(data->conn->scope_id) + return data->conn->scope_id; + /* NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign) */ + scope_id = sa6->sin6_scope_id; + if(!scope_id && IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { + /* The resolver did not set scope_id for this link-local address. + * Try to determine it from the system's network interfaces. + * Without a scope_id, connect() to a link-local address fails + * with EINVAL on Linux. + * NOTE: On multi-homed hosts with several interfaces having + * link-local addresses, this picks the first one found, which + * may not be the correct outgoing interface. */ +#if defined(HAVE_GETIFADDRS) && defined(HAVE_NET_IF_H) + struct ifaddrs *ifa, *ifa_list; + if(getifaddrs(&ifa_list) == 0) { + for(ifa = ifa_list; ifa; ifa = ifa->ifa_next) { + if(ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6 && + (ifa->ifa_flags & IFF_UP) && + !(ifa->ifa_flags & IFF_LOOPBACK)) { + struct sockaddr_in6 *s6 = (void *)ifa->ifa_addr; + if(IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr) && s6->sin6_scope_id) { + scope_id = s6->sin6_scope_id; + infof(data, + "determined scope_id=%lu for link-local address " + "from local interface", + (unsigned long)scope_id); + break; + } + } + } + freeifaddrs(ifa_list); + } +#endif /* HAVE_GETIFADDRS && HAVE_NET_IF_H */ + } + return scope_id; +} +#endif + static CURLcode socket_open(struct Curl_easy *data, struct Curl_sockaddr_ex *addr, curl_socket_t *sockfd) @@ -371,9 +477,9 @@ static CURLcode socket_open(struct Curl_easy *data, #endif #if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) - if(data->conn->scope_id && (addr->family == AF_INET6)) { + if(addr->family == AF_INET6) { struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr; - sa6->sin6_scope_id = data->conn->scope_id; + sa6->sin6_scope_id = get_scope_id(data, sa6); } #endif return CURLE_OK; @@ -511,8 +617,7 @@ CURLcode Curl_parse_interface(const char *input, ++host_part; *host = curlx_memdup0(host_part, len - (host_part - input)); if(!*host) { - curlx_free(*iface); - *iface = NULL; + curlx_safefree(*iface); return CURLE_OUT_OF_MEMORY; } return CURLE_OK; @@ -547,7 +652,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, const char *host_input = data->set.str[STRING_BINDHOST]; const char *iface = iface_input ? iface_input : dev; const char *host = host_input ? host_input : dev; - int error; + int sockerr; #ifdef IP_BIND_ADDRESS_NO_PORT int on = 1; #endif @@ -610,9 +715,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, 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; + data->state.os_errno = sockerr = SOCKERRNO; failf(data, "Could not bind to interface '%s' with errno %d: %s", - iface, error, curlx_strerror(error, buffer, sizeof(buffer))); + iface, sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); return CURLE_INTERFACE_FAILED; } break; @@ -624,7 +729,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, * 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", + infof(data, "Local Interface %s is ip %s using address family %d", iface, host, af); done = 1; break; @@ -650,7 +755,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, 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", + infof(data, "Name '%s' family %d resolved to '%s' family %d", host, af, myhost, h_af); Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */ if(af != h_af) { @@ -714,9 +819,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, generic resolve error. */ char buffer[STRERROR_LEN]; data->state.errorbuf = FALSE; - data->state.os_errno = error = SOCKERRNO; + data->state.os_errno = sockerr = SOCKERRNO; failf(data, "Could not bind to '%s' with errno %d: %s", host, - error, curlx_strerror(error, buffer, sizeof(buffer))); + sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); return CURLE_INTERFACE_FAILED; } } @@ -765,9 +870,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, } { char buffer[STRERROR_LEN]; - data->state.os_errno = error = SOCKERRNO; + data->state.os_errno = sockerr = SOCKERRNO; failf(data, "bind failed with errno %d: %s", - error, curlx_strerror(error, buffer, sizeof(buffer))); + sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); } return CURLE_INTERFACE_FAILED; @@ -777,12 +882,12 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, /* * verifyconnect() returns TRUE if the connect really has happened. */ -static bool verifyconnect(curl_socket_t sockfd, int *error) +static bool verifyconnect(curl_socket_t sockfd, int *psockerr) { bool rc = TRUE; #ifdef SO_ERROR - int err = 0; - curl_socklen_t errSize = sizeof(err); + int sockerr = 0; + curl_socklen_t errSize = sizeof(sockerr); #ifdef _WIN32 /* @@ -803,27 +908,27 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) SleepEx(0, FALSE); #endif - if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) - err = SOCKERRNO; + if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &errSize)) + sockerr = SOCKERRNO; #if defined(EBADIOCTL) && defined(__minix) /* Minix 3.1.x does not support getsockopt on UDP sockets */ - if(EBADIOCTL == err) { + if(EBADIOCTL == sockerr) { SET_SOCKERRNO(0); - err = 0; + sockerr = 0; } #endif - if((err == 0) || (SOCKEISCONN == err)) + if((sockerr == 0) || (SOCKEISCONN == sockerr)) /* we are connected, awesome! */ rc = TRUE; else /* This was not a successful connect */ rc = FALSE; - if(error) - *error = err; + if(psockerr) + *psockerr = sockerr; #else (void)sockfd; - if(error) - *error = SOCKERRNO; + if(psockerr) + *psockerr = SOCKERRNO; #endif return rc; } @@ -832,38 +937,25 @@ static bool verifyconnect(curl_socket_t sockfd, int *error) * Determine the curl code for a socket connect() == -1 with errno. */ static CURLcode socket_connect_result(struct Curl_easy *data, - const char *ipaddress, int error) + const char *ipaddress, int sockerr) { - switch(error) { - case SOCKEINPROGRESS: - case SOCKEWOULDBLOCK: -#ifdef EAGAIN -#if (EAGAIN) != (SOCKEWOULDBLOCK) - /* On some platforms EAGAIN and EWOULDBLOCK are the - * same value, and on others they are different, hence - * the odd #if - */ - case EAGAIN: -#endif -#endif + if(sockerr == SOCKEINPROGRESS || SOCK_EAGAIN(sockerr)) return CURLE_OK; - default: - /* unknown error, fallthrough and try another address! */ - { - VERBOSE(char buffer[STRERROR_LEN]); - infof(data, "Immediate connect fail for %s: %s", ipaddress, - curlx_strerror(error, buffer, sizeof(buffer))); - NOVERBOSE((void)ipaddress); - } - data->state.os_errno = error; - /* connect failed */ - return CURLE_COULDNT_CONNECT; + /* unknown error, fallthrough and try another address! */ + { + VERBOSE(char buffer[STRERROR_LEN]); + infof(data, "Immediate connect fail for %s: %s", ipaddress, + curlx_strerror(sockerr, buffer, sizeof(buffer))); + NOVERBOSE((void)ipaddress); } + data->state.os_errno = sockerr; + /* connect failed */ + return CURLE_COULDNT_CONNECT; } struct cf_socket_ctx { - uint8_t transport; + struct Curl_peer *peer; 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) */ @@ -874,13 +966,14 @@ struct cf_socket_ctx { struct curltime last_sndbuf_query_at; /* when SO_SNDBUF last queried */ ULONG sndbuf_size; /* the last set SO_SNDBUF size */ #endif - int error; /* errno of last failure or 0 */ + int sockerr; /* socket error of last failure or 0 */ #ifdef DEBUGBUILD 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 */ #endif + uint8_t transport; BIT(got_first_byte); /* if first byte was received */ BIT(listening); /* socket is listening */ BIT(accepted); /* socket was accepted, not connected */ @@ -889,10 +982,12 @@ struct cf_socket_ctx { }; static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, + struct Curl_peer *peer, struct Curl_sockaddr_ex *addr, uint8_t transport) { memset(ctx, 0, sizeof(*ctx)); + Curl_peer_link(&ctx->peer, peer); ctx->sock = CURL_SOCKET_BAD; ctx->transport = transport; ctx->addr = *addr; @@ -929,22 +1024,12 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, return CURLE_OK; } -static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) +static void cf_socket_ctx_free(struct cf_socket_ctx *ctx) { - struct cf_socket_ctx *ctx = cf->ctx; - - 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; - socket_close(data, cf->conn, !ctx->accepted, ctx->sock); - ctx->sock = CURL_SOCKET_BAD; - ctx->active = FALSE; - memset(&ctx->started_at, 0, sizeof(ctx->started_at)); - memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); + if(ctx) { + Curl_peer_unlink(&ctx->peer); + curlx_free(ctx); } - - cf->connected = FALSE; } static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf, @@ -973,10 +1058,16 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; - cf_socket_close(cf, data); CURL_TRC_CF(data, cf, "destroy"); - curlx_free(ctx); - cf->ctx = NULL; + if(ctx) { + if(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; + socket_close(data, cf->conn, !ctx->accepted, ctx->sock); + } + cf_socket_ctx_free(ctx); + } } static void set_local_ip(struct Curl_cfilter *cf, @@ -996,12 +1087,12 @@ static void set_local_ip(struct Curl_cfilter *cf, memset(&ssloc, 0, sizeof(ssloc)); if(getsockname(ctx->sock, (struct sockaddr *)&ssloc, &slen)) { - VERBOSE(int error = SOCKERRNO); + VERBOSE(int sockerr = SOCKERRNO); infof(data, "getsockname() failed with errno %d: %s", - error, curlx_strerror(error, buffer, sizeof(buffer))); + sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); } - else if(!Curl_addr2string((struct sockaddr *)&ssloc, slen, - ctx->ip.local_ip, &ctx->ip.local_port)) { + else if(!sockaddr2string((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))); } @@ -1018,12 +1109,15 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, /* 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)) { + if(!sockaddr2string(&ctx->addr.curl_sa_addr, + (curl_socklen_t)ctx->addr.addrlen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { char buffer[STRERROR_LEN]; - ctx->error = errno; + /* using bare errno instead of SOCKERRNO is safe here, because + sockaddr2string() calls curlx_inet_ntop(), and they both report failures + via errno (even on Windows builds). */ + ctx->sockerr = errno; /* malformed address or bug in inet_ntop, try next address */ failf(data, "curl_sa_addr inet_ntop() failed with errno %d: %s", errno, curlx_strerror(errno, buffer, sizeof(buffer))); @@ -1091,7 +1185,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, (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); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + { + struct sockaddr_in6 *sa6 = (void *)&ctx->addr.curl_sa_addr; + if(sa6->sin6_scope_id) + infof(data, " Trying [%s]:%d scope_id=%lu...", + ctx->ip.remote_ip, ctx->ip.remote_port, + (unsigned long)sa6->sin6_scope_id); + else +#endif + infof(data, " Trying [%s]:%d...", + ctx->ip.remote_ip, ctx->ip.remote_port); +#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID + } +#endif } else #endif @@ -1154,7 +1261,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, error = curlx_nonblock(ctx->sock, TRUE); if(error < 0) { result = CURLE_UNSUPPORTED_PROTOCOL; - ctx->error = SOCKERRNO; + ctx->sockerr = SOCKERRNO; goto out; } #else @@ -1164,7 +1271,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, error = curlx_nonblock(ctx->sock, TRUE); if(error < 0) { result = CURLE_UNSUPPORTED_PROTOCOL; - ctx->error = SOCKERRNO; + ctx->sockerr = SOCKERRNO; goto out; } } @@ -1183,7 +1290,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, cf->connected = TRUE; } CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T, - result, ctx->sock); + (int)result, ctx->sock); return result; } @@ -1257,7 +1364,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, *done = FALSE; /* a negative world view is best */ if(ctx->sock == CURL_SOCKET_BAD) { - int error; + int sockerr; result = cf_socket_open(cf, data); if(result) @@ -1270,12 +1377,13 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, /* Connect TCP socket */ rc = do_connect(cf, data, (bool)cf->conn->bits.tcp_fastopen); - error = SOCKERRNO; + sockerr = 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(rc == -1) { - result = socket_connect_result(data, ctx->ip.remote_ip, error); + ctx->sockerr = sockerr; + result = socket_connect_result(data, ctx->ip.remote_ip, sockerr); goto out; } } @@ -1295,7 +1403,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, return CURLE_OK; } else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { - if(verifyconnect(ctx->sock, &ctx->error)) { + if(verifyconnect(ctx->sock, &ctx->sockerr)) { /* we are connected with TCP, awesome! */ ctx->connected_at = *Curl_pgrs_now(data); set_local_ip(cf, data); @@ -1307,7 +1415,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, } else if(rc & CURL_CSELECT_ERR) { CURL_TRC_CF(data, cf, "poll/select error on fd=%" FMT_SOCKET_T, ctx->sock); - (void)verifyconnect(ctx->sock, &ctx->error); + (void)verifyconnect(ctx->sock, &ctx->sockerr); result = CURLE_COULDNT_CONNECT; } @@ -1315,10 +1423,10 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, if(result) { VERBOSE(char buffer[STRERROR_LEN]); set_local_ip(cf, data); - if(ctx->error) { - data->state.os_errno = ctx->error; - SET_SOCKERRNO(ctx->error); - VERBOSE(curlx_strerror(ctx->error, buffer, sizeof(buffer))); + if(ctx->sockerr) { + data->state.os_errno = ctx->sockerr; + SET_SOCKERRNO(ctx->sockerr); + VERBOSE(curlx_strerror(ctx->sockerr, buffer, sizeof(buffer))); } else { VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed"))); @@ -1330,7 +1438,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, 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))); + curlx_strerror(ctx->sockerr, buffer, sizeof(buffer))); *done = FALSE; } return result; @@ -1369,7 +1477,7 @@ static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf, #ifdef USE_WINSOCK -/* Offered by mingw-w64 v13+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v13+, MS SDK 7.0A/VS2010+ */ #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B #endif @@ -1442,22 +1550,12 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(!curlx_sztouz(rv, pnwritten)) { int sockerr = SOCKERRNO; - - if( -#ifdef USE_WINSOCK - /* 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) + if(SOCK_EAGAIN(sockerr) +#ifndef USE_WINSOCK + || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS) #endif ) { - /* EWOULDBLOCK */ - result = CURLE_AGAIN; + result = CURLE_AGAIN; /* EWOULDBLOCK */ } else { char buffer[STRERROR_LEN]; @@ -1474,7 +1572,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, #endif CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu", - orig_len, result, *pnwritten); + orig_len, (int)result, *pnwritten); cf->conn->sock[cf->sockindex] = fdsave; return result; } @@ -1508,21 +1606,12 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(!curlx_sztouz(rv, pnread)) { int sockerr = SOCKERRNO; - - if( -#ifdef USE_WINSOCK - /* 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) + if(SOCK_EAGAIN(sockerr) +#ifndef USE_WINSOCK + || (sockerr == SOCKEINTR) #endif ) { - /* EWOULDBLOCK */ - result = CURLE_AGAIN; + result = CURLE_AGAIN; /* EWOULDBLOCK */ } else { char buffer[STRERROR_LEN]; @@ -1533,7 +1622,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } } - CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread); + CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, (int)result, *pnread); if(!result && !ctx->got_first_byte) { ctx->first_byte_at = *Curl_pgrs_now(data); ctx->got_first_byte = TRUE; @@ -1548,8 +1637,6 @@ static void cf_socket_update_data(struct Curl_cfilter *cf, if(cf->connected && (cf->sockindex == FIRSTSOCKET)) { struct cf_socket_ctx *ctx = cf->ctx; data->info.primary = ctx->ip; - /* not sure if this is redundant... */ - data->info.conn_remote_port = cf->conn->remote_port; } } @@ -1696,7 +1783,6 @@ struct Curl_cftype Curl_cft_tcp = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -1710,17 +1796,24 @@ struct Curl_cftype Curl_cft_tcp = { CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - DEBUGASSERT(transport == TRNSPRT_TCP); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_TCP); if(!addr) { result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; @@ -1732,7 +1825,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -1742,7 +1835,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; @@ -1837,7 +1930,8 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf, if(ctx->sock == CURL_SOCKET_BAD) { result = cf_socket_open(cf, data); if(result) { - CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result); + CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", + (int)result); goto out; } @@ -1862,7 +1956,6 @@ struct Curl_cftype Curl_cft_udp = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_udp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -1876,24 +1969,31 @@ struct Curl_cftype Curl_cft_udp = { CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_UDP || transport_peer == TRNSPRT_QUIC); ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -1903,7 +2003,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; @@ -1916,7 +2016,6 @@ struct Curl_cftype Curl_cft_unix = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, @@ -1929,25 +2028,32 @@ struct Curl_cftype Curl_cft_unix = { }; CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - struct Curl_sockaddr_ex *addr, - uint8_t transport) + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { struct cf_socket_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; CURLcode result; (void)data; + (void)origin; (void)conn; - DEBUGASSERT(transport == TRNSPRT_UNIX); + (void)tunnel_peer; + (void)tunnel_transport; + DEBUGASSERT(transport_peer == TRNSPRT_UNIX); ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - result = cf_socket_ctx_init(ctx, addr, transport); + result = cf_socket_ctx_init(ctx, peer, addr, transport_peer); if(result) goto out; @@ -1957,7 +2063,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, *pcf = (!result) ? cf : NULL; if(result) { curlx_safefree(cf); - curlx_safefree(ctx); + cf_socket_ctx_free(ctx); } return result; @@ -2005,13 +2111,13 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf, plen = sizeof(ssrem); memset(&ssrem, 0, plen); if(getpeername(ctx->sock, (struct sockaddr *)&ssrem, &plen)) { - int error = SOCKERRNO; + int sockerr = SOCKERRNO; failf(data, "getpeername() failed with errno %d: %s", - error, curlx_strerror(error, buffer, sizeof(buffer))); + sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer))); return; } - if(!Curl_addr2string((struct sockaddr *)&ssrem, plen, - ctx->ip.remote_ip, &ctx->ip.remote_port)) { + if(!sockaddr2string((struct sockaddr *)&ssrem, plen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { failf(data, "ssrem inet_ntop() failed with errno %d: %s", errno, curlx_strerror(errno, buffer, sizeof(buffer))); return; @@ -2058,7 +2164,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 = SOCKET_READABLE(ctx->sock, 0); - CURL_TRC_CF(data, cf, "socket_check -> %x", socketstate); + CURL_TRC_CF(data, cf, "socket_check -> %x", (unsigned int)socketstate); switch(socketstate) { case -1: /* error */ /* let's die here */ @@ -2128,10 +2234,10 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, int error = 0; /* activate callback for setting socket options */ - Curl_set_in_callback(data, true); + Curl_set_in_callback(data, TRUE); error = data->set.fsockopt(data->set.sockopt_client, ctx->sock, CURLSOCKTYPE_ACCEPT); - Curl_set_in_callback(data, false); + Curl_set_in_callback(data, FALSE); if(error) return CURLE_ABORTED_BY_CALLBACK; @@ -2146,7 +2252,6 @@ struct Curl_cftype Curl_cft_tcp_accept = { CURL_LOG_LVL_NONE, cf_socket_destroy, cf_tcp_accept_connect, - cf_socket_close, cf_socket_shutdown, cf_socket_adjust_pollset, Curl_cf_def_data_pending, diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 40c001cc14fd..37ddc02576b3 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -25,8 +25,6 @@ ***************************************************************************/ #include "curl_setup.h" -#include "sockaddr.h" /* required for Curl_sockaddr_storage */ - struct Curl_addrinfo; struct Curl_cfilter; struct Curl_easy; @@ -34,23 +32,6 @@ struct connectdata; struct Curl_sockaddr_ex; struct ip_quadruple; -/* - * 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. - */ -struct Curl_sockaddr_ex { - int family; - int socktype; - int protocol; - unsigned int addrlen; - union { - struct sockaddr sa; - struct Curl_sockaddr_storage buf; - } addr; -}; #define curl_sa_addr addr.sa #define curl_sa_addrbuf addr.buf @@ -94,9 +75,13 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, */ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that opens a UDP socket to the given address @@ -107,9 +92,13 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, */ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that opens a UNIX socket to the given address @@ -120,9 +109,13 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, */ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); /** * Creates a cfilter that keeps a listening socket. diff --git a/lib/cfilters.c b/lib/cfilters.c index 6d7d8ef7385e..fb1914d133e8 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -33,16 +33,26 @@ #include "select.h" #include "curlx/strparse.h" -#ifdef UNITTESTS -/* @unittest 2600 */ -UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); -UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) +CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); + CURLcode result; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + if(cf->next) { + result = cf->next->cft->do_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + cf->connected = TRUE; + *done = TRUE; + return CURLE_OK; } -#endif CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -117,29 +127,28 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, } #ifdef CURLVERBOSE -static void conn_trc_filters(struct Curl_easy *data, - int sockindex, - const char *info) +void Curl_conn_trc_filters(struct Curl_easy *data, + int sockindex, const char *info) { if(CURL_TRC_M_is_verbose(data) && data->conn) { struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; if(cf) { - struct dynbuf msg; - CURLcode result = CURLE_OK; - - curlx_dyn_init(&msg, 1024); - result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex); - for(; cf && !result; cf = cf->next) { - result = curlx_dyn_addf(&msg, "[%s%s]", - cf->connected ? "" : "!", cf->cft->name); + char msg[256], *buf; + int blen, n; + + buf = msg; + blen = sizeof(msg) - 1; + n = curl_msnprintf(buf, blen, "%s [%d]", info, sockindex); + buf += n; + blen -= n; + for(; cf && blen; cf = cf->next) { + n = curl_msnprintf(buf, blen, "[%s%s]", + cf->connected ? "" : "!", cf->cft->name); + buf += n; + blen -= n; } - if(!result) - CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg)); - else - CURL_TRC_M(data, "%s [%d] error %d tracing chain", - info, sockindex, result); - curlx_dyn_free(&msg); + CURL_TRC_M(data, "%s%s", msg, blen ? "" : "..."); } else CURL_TRC_M(data, "%s [%d][-]", info, sockindex); @@ -170,22 +179,11 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, void Curl_conn_cf_discard_all(struct Curl_easy *data, struct connectdata *conn, int sockindex) { + struct curltime *pt = &conn->shutdown.start[sockindex]; + memset(pt, 0, sizeof(*pt)); Curl_conn_cf_discard_chain(&conn->cfilter[sockindex], data); } -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[sockindex]; - if(cf) { - cf->cft->do_close(cf, data); - } - Curl_shutdown_clear(data, sockindex); -} - CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) { struct Curl_cfilter *cf; @@ -225,7 +223,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) bool cfdone = FALSE; result = cf->cft->do_shutdown(cf, data, &cfdone); if(result) { - CURL_TRC_CF(data, cf, "shut down failed with %d", result); + CURL_TRC_CF(data, cf, "shut down failed with %d", (int)result); return result; } else if(!cfdone) { @@ -427,12 +425,6 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } -void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - if(cf) - cf->cft->do_close(cf, data); -} - CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const uint8_t *buf, size_t len, bool eos, size_t *pnwritten) @@ -452,29 +444,6 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, return CURLE_RECV_ERROR; } -#ifdef CURLVERBOSE -static CURLcode cf_verboseconnect(struct Curl_easy *data, - struct Curl_cfilter *cf) -{ - if(Curl_trc_is_verbose(data)) { - struct ip_quadruple ipquad; - bool is_ipv6; - CURLcode result; - - result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad); - if(result) - return result; - - infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ", - (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", - CURL_CONN_HOST_DISPNAME(data->conn), - ipquad.remote_ip, ipquad.remote_port, - ipquad.local_ip, ipquad.local_port); - } - return CURLE_OK; -} -#endif - static CURLcode cf_cntrl_all(struct connectdata *conn, struct Curl_easy *data, bool ignore_result, @@ -492,36 +461,14 @@ static CURLcode cf_cntrl_all(struct connectdata *conn, return result; } -static void cf_cntrl_update_info(struct Curl_easy *data, - struct connectdata *conn) +void Curl_conn_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) +void Curl_conn_remove_setup_filters(struct Curl_easy *data, + int sockindex) { struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex]; while(*anchor) { @@ -538,123 +485,11 @@ static void conn_remove_setup_filters(struct Curl_easy *data, } } -CURLcode Curl_conn_connect(struct Curl_easy *data, - int sockindex, - bool blocking, - bool *done) -{ -#define CF_CONN_NUM_POLLS_ON_STACK 5 - struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK]; - struct easy_pollset ps; - struct curl_pollfds cpfds; - struct Curl_cfilter *cf; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - DEBUGASSERT(data->conn); - 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 = (bool)cf->connected; - if(*done) - return CURLE_OK; - - Curl_pollset_init(&ps); - Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK); - while(!*done) { - if(Curl_conn_needs_flush(data, sockindex)) { - DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex)); - result = Curl_conn_flush(data, sockindex); - if(result && (result != CURLE_AGAIN)) - return result; - } - - result = cf->cft->do_connect(cf, data, done); - CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d", - blocking, result, *done); - if(!result && *done) { - /* Now that the complete filter chain is connected, let all filters - * 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(cf, data); - data->conn->keepalive = *Curl_pgrs_now(data); - VERBOSE(result = cf_verboseconnect(data, cf)); - VERBOSE(conn_trc_filters(data, sockindex, "connected")); - conn_remove_setup_filters(data, sockindex); - VERBOSE(conn_trc_filters(data, sockindex, "reduced to")); - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result); - VERBOSE(conn_trc_filters(data, sockindex, "failed to connect")); - conn_report_connect_stats(cf, data); - goto out; - } - - if(!blocking) - goto out; - else { - /* check allowed time left */ - const timediff_t timeout_ms = Curl_timeleft_ms(data); - curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); - int rc; - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "connect timeout"); - result = CURLE_OPERATION_TIMEDOUT; - goto out; - } - - CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll"); - Curl_pollset_reset(&ps); - Curl_pollfds_reset(&cpfds); - /* In general, we want to send after connect, wait on that. */ - if(sockfd != CURL_SOCKET_BAD) - 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); - if(result) - goto out; - - rc = Curl_poll(cpfds.pfds, cpfds.n, - CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10))); - CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d", - rc); - if(rc < 0) { - result = CURLE_COULDNT_CONNECT; - goto out; - } - /* continue iterating */ - } - } - -out: - Curl_pollset_cleanup(&ps); - Curl_pollfds_cleanup(&cpfds); - return result; -} - bool Curl_conn_is_setup(struct connectdata *conn, int sockindex) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; - return (conn->cfilter[sockindex] != NULL); + return !!conn->cfilter[sockindex]; } bool Curl_conn_is_connected(struct connectdata *conn, int sockindex) @@ -688,10 +523,37 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) return FALSE; } +#ifndef CURL_DISABLE_PROXY +static bool cf_is_tunneling(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if((cf->cft->flags & CF_TYPE_PROXY)) + return TRUE; + } + return FALSE; +} + +bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex) +{ + if(!CONN_SOCK_IDX_VALID(sockindex)) + return FALSE; + return conn ? cf_is_tunneling(conn->cfilter[sockindex]) : FALSE; +} +#else +bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex) +{ + (void)conn; + (void)sockindex; + return FALSE; +} +#endif /* CURL_DISABLE_PROXY */ + static bool cf_is_ssl(struct Curl_cfilter *cf) { for(; cf; cf = cf->next) { - if(cf->cft->flags & CF_TYPE_SSL) + /* A tunneling proxy does not offer end2end encryption, even if + * it does SSL itself (e.g. QUIC H3 proxy) */ + if((cf->cft->flags & CF_TYPE_SSL) && !(cf->cft->flags & CF_TYPE_PROXY)) return TRUE; if(cf->cft->flags & CF_TYPE_IP_CONNECT) return FALSE; @@ -890,8 +752,19 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data, DEBUGASSERT(data); DEBUGASSERT(conn); - for(i = 0; (i < 2) && !result; ++i) { - result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); + /* During connect time, connection filters may add sockets to the pollset + * even when the transfer neither wants to send nor receive. And those + * sockets, when having events, are served. + * Once connected however, a transfer that neither wants to send nor receive + * will never call the connection filters. Any sockets added by the filters + * will not change state and POLLIN/POLLOUT events will trigger forever, + * making us busy loop. See #21671 */ + if(ps->n || !Curl_conn_is_connected(conn, FIRSTSOCKET) || + (conn->cfilter[SECONDARYSOCKET] && + !Curl_conn_is_connected(conn, SECONDARYSOCKET))) { + for(i = 0; (i < 2) && !result && conn; ++i) { + result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps); + } } return result; } @@ -953,8 +826,8 @@ void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, &portarg, CURL_UNCONST(phost))) { /* Everything connected or query unsuccessful, the overall * connection's destination is the answer */ - *phost = data->conn->host.name; - portarg = data->conn->remote_port; + *phost = data->conn->origin->hostname; + portarg = data->conn->origin->port; } if(pport) *pport = portarg; @@ -1018,8 +891,8 @@ const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, return NULL; } -static const struct Curl_sockaddr_ex * -cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data) +static const struct Curl_sockaddr_ex *cf_get_remote_addr( + struct Curl_cfilter *cf, struct Curl_easy *data) { const struct Curl_sockaddr_ex *remote_addr = NULL; if(cf && @@ -1057,8 +930,8 @@ curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data) return data->conn->sock[FIRSTSOCKET]; } -const struct Curl_sockaddr_ex * -Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex) +const struct Curl_sockaddr_ex *Curl_conn_get_remote_addr( + struct Curl_easy *data, int sockindex) { struct Curl_cfilter *cf = (data->conn && CONN_SOCK_IDX_VALID(sockindex)) ? diff --git a/lib/cfilters.h b/lib/cfilters.h index ac56737ccb99..b1aad029e070 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -39,10 +39,6 @@ struct curl_tlssessioninfo; typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf, struct Curl_easy *data); -/* Callback to close the connection immediately. */ -typedef void Curl_cft_close(struct Curl_cfilter *cf, - struct Curl_easy *data); - /* Callback to close the connection filter gracefully, non-blocking. * Implementations MUST NOT chain calls to cf->next. */ @@ -218,7 +214,6 @@ struct Curl_cftype { 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 */ @@ -270,6 +265,8 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, int query, int *pres1, void *pres2); CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done); +CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done); /** * Create a new filter instance, unattached to the filter chain. @@ -322,7 +319,6 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data, 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 uint8_t *buf, size_t len, bool eos, size_t *pnwritten); @@ -364,16 +360,6 @@ bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 -/** - * Bring the filter chain at `sockindex` for connection `data->conn` into - * connected state. Which will set `*done` to TRUE. - * This can be called on an already connected chain with no side effects. - * When not `blocking`, calls may return without error and `*done != TRUE`, - * while the individual filters negotiated the connection. - */ -CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, - bool blocking, bool *done); - /** * Check if a filter chain at `sockindex` for connection `conn` exists. */ @@ -399,6 +385,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); */ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); +/* Determine if the connection has one or more proxy filters. + * e.g. is tunneling. */ +bool Curl_conn_is_tunneling(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 @@ -433,11 +423,11 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data, const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, struct connectdata *conn); -/** - * Close the filter chain at `sockindex` for connection `data->conn`. - * Filters remain in place and may be connected again afterwards. - */ -void Curl_conn_close(struct Curl_easy *data, int sockindex); +void Curl_conn_cntrl_update_info(struct Curl_easy *data, + struct connectdata *conn); + +void Curl_conn_remove_setup_filters(struct Curl_easy *data, + int sockindex); /** * Shutdown the connection at `sockindex` non-blocking, using timeout @@ -582,7 +572,7 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, * Get the remote hostname and port that the connection is currently * talking to (or will talk to). * Once connected or before connect starts, - * it is `conn->host.name` and `conn->remote_port`. + * it is `conn->origin->hostname` and `conn->origin->port`. * During connect, when tunneling proxies are involved (http or socks), * it will be the name and port the proxy currently negotiates with. */ @@ -604,6 +594,11 @@ int Curl_conn_get_stream_error(struct Curl_easy *data, struct connectdata *conn, int sockindex); +#ifdef CURLVERBOSE +void Curl_conn_trc_filters(struct Curl_easy *data, + int sockindex, const char *info); +#endif + /** * Get the index of the given socket in the connection's sockets. * Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the @@ -664,7 +659,7 @@ struct cf_call_data { #define CF_DATA_SAVE(save, cf, data) \ do { \ (save) = CF_CTX_CALL_DATA(cf); \ - DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + DEBUGASSERT(!(save).data || (save).depth > 0); \ CF_CTX_CALL_DATA(cf).depth++; \ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \ } while(0) @@ -672,7 +667,7 @@ struct cf_call_data { #define CF_DATA_RESTORE(cf, save) \ do { \ DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \ - DEBUGASSERT((save).data == NULL || (save).depth > 0); \ + DEBUGASSERT(!(save).data || (save).depth > 0); \ CF_CTX_CALL_DATA(cf) = (save); \ } while(0) diff --git a/lib/config-os400.h b/lib/config-os400.h index 3d8d59c97e4b..76841af202ec 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -34,11 +34,10 @@ /* Global configuration parameters: normally generated by autoconf. */ /* ---------------------------------------------------------------- */ -/* Location of default ca bundle */ +/* 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" - /* Definition to make a library symbol externally visible. */ #define CURL_EXTERN_SYMBOL @@ -239,7 +238,7 @@ /* 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 +/* Define to 1 if all of the C89 standard headers exist (not only the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #define STDC_HEADERS 1 diff --git a/lib/conncache.c b/lib/conncache.c index 33fb68b124c6..5ba236862200 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -122,7 +122,7 @@ void Curl_cpool_init(struct cpool *cpool, cpool->idata = idata; cpool->share = share; - cpool->initialised = TRUE; + cpool->initialized = TRUE; } /* Return the "first" connection in the pool or NULL. */ @@ -230,7 +230,7 @@ static void cpool_discard_conn(struct cpool *cpool, void Curl_cpool_destroy(struct cpool *cpool) { - if(cpool && cpool->initialised && cpool->idata) { + if(cpool && cpool->initialized && cpool->idata) { struct connectdata *conn; struct Curl_sigpipe_ctx pipe_ctx; @@ -305,9 +305,9 @@ static struct cpool_bundle *cpool_add_bundle(struct cpool *cpool, return bundle; } -static struct connectdata * -cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle, - const struct curltime *pnow) +static struct connectdata *cpool_bundle_get_oldest_idle( + struct cpool_bundle *bundle, + const struct curltime *pnow) { struct Curl_llist_node *curr; timediff_t highscore = -1; diff --git a/lib/conncache.h b/lib/conncache.h index 7cee4d4729a5..71940d724c02 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -56,7 +56,7 @@ struct cpool { struct Curl_easy *idata; /* internal handle for maintenance */ struct Curl_share *share; /* != NULL if pool belongs to share */ BIT(locked); - BIT(initialised); + BIT(initialized); }; /* Init the pool, pass multi only if pool is owned by it. diff --git a/lib/connect.c b/lib/connect.c index b13d496848a1..a2b86c74587d 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -23,51 +23,20 @@ ***************************************************************************/ #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) -#include -#endif -#ifdef HAVE_SYS_IOCTL_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_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 "cf-setup.h" #include "multiif.h" -#include "curlx/inet_ntop.h" -#include "curlx/strparse.h" -#include "vtls/vtls.h" /* for vtls cfilters */ #include "progress.h" #include "conncache.h" #include "multihandle.h" -#include "http_proxy.h" -#include "socks.h" +#include "select.h" +#include "curlx/strparse.h" #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR) @@ -210,59 +179,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex) 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, uint16_t *port) -{ - struct sockaddr_in *si = NULL; -#ifdef USE_IPV6 - struct sockaddr_in6 *si6 = NULL; -#endif -#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)) { - *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 -#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; - errno = SOCKEAFNOSUPPORT; - return FALSE; -} - /* * Used to extract socket and connectdata struct for the most recent * transfer on the given Curl_easy. @@ -325,267 +241,22 @@ void Curl_conncontrol(struct connectdata *conn, } } -typedef enum { - CF_SETUP_INIT, - CF_SETUP_CNNCT_EYEBALLS, - CF_SETUP_CNNCT_SOCKS, - CF_SETUP_CNNCT_HTTP_PROXY, - CF_SETUP_CNNCT_HAPROXY, - CF_SETUP_CNNCT_SSL, - CF_SETUP_DONE -} cf_setup_state; - -struct cf_setup_ctx { - cf_setup_state state; - int ssl_mode; - uint8_t transport; -}; - -static CURLcode cf_setup_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - struct cf_setup_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - if(cf->connected) { - *done = TRUE; - return CURLE_OK; - } - - /* connect current sub-chain */ -connect_sub_chain: - - if(cf->next && !cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; - } - - if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { - result = cf_ip_happy_insert_after(cf, data, ctx->transport); - if(result) - return result; - ctx->state = CF_SETUP_CNNCT_EYEBALLS; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } - - /* sub-chain connected, do we need to add more? */ -#ifndef CURL_DISABLE_PROXY - if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - const char *hostname = - cf->conn->bits.httpproxy ? - cf->conn->http_proxy.host.name : - cf->conn->bits.conn_to_host ? - cf->conn->conn_to_host.name : - cf->sockindex == SECONDARYSOCKET ? - cf->conn->secondaryhostname : cf->conn->host.name; - uint16_t port = - cf->conn->bits.httpproxy ? cf->conn->http_proxy.port : - cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port : - cf->conn->bits.conn_to_port ? cf->conn->conn_to_port : - cf->conn->remote_port; - const char *user = cf->conn->socks_proxy.user; - const char *passwd = cf->conn->socks_proxy.passwd; - - result = Curl_cf_socks_proxy_insert_after( - cf, data, hostname, port, cf->conn->ip_version, - cf->conn->socks_proxy.proxytype, user, passwd); - if(result) - return result; - ctx->state = CF_SETUP_CNNCT_SOCKS; - if(!cf->next || !cf->next->connected) - goto 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)) { - result = Curl_cf_ssl_proxy_insert_after(cf, data); - if(result) - return result; - } -#endif /* USE_SSL */ - -#ifndef CURL_DISABLE_HTTP - if(cf->conn->bits.tunnel_proxy) { - result = Curl_cf_http_proxy_insert_after(cf, data); - if(result) - return result; - } -#endif /* !CURL_DISABLE_HTTP */ - ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } -#endif /* !CURL_DISABLE_PROXY */ - - if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { -#ifndef CURL_DISABLE_PROXY - if(data->set.haproxyprotocol) { - if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - failf(data, "haproxy protocol not supported with SSL " - "encryption in place (QUIC?)"); - return CURLE_UNSUPPORTED_PROTOCOL; - } - result = Curl_cf_haproxy_insert_after(cf, data); - if(result) - return result; - } -#endif /* !CURL_DISABLE_PROXY */ - ctx->state = CF_SETUP_CNNCT_HAPROXY; - if(!cf->next || !cf->next->connected) - goto 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->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; - } -#endif /* USE_SSL */ - ctx->state = CF_SETUP_CNNCT_SSL; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } - - ctx->state = CF_SETUP_DONE; - cf->connected = TRUE; - *done = TRUE; - return CURLE_OK; -} - -static void cf_setup_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_setup_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - ctx->state = CF_SETUP_INIT; - - if(cf->next) { - cf->next->cft->do_close(cf->next, data); - Curl_conn_cf_discard_chain(&cf->next, data); - } -} - -static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_setup_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "destroy"); - curlx_safefree(ctx); -} - -struct Curl_cftype Curl_cft_setup = { - "SETUP", - CF_TYPE_SETUP, - CURL_LOG_LVL_NONE, - cf_setup_destroy, - cf_setup_connect, - cf_setup_close, - Curl_cf_def_shutdown, - Curl_cf_def_adjust_pollset, - Curl_cf_def_data_pending, - Curl_cf_def_send, - Curl_cf_def_recv, - Curl_cf_def_cntrl, - Curl_cf_def_conn_is_alive, - Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, -}; - -static CURLcode cf_setup_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - uint8_t transport, - int ssl_mode) -{ - struct Curl_cfilter *cf = NULL; - struct cf_setup_ctx *ctx; - CURLcode result = CURLE_OK; - - (void)data; - ctx = curlx_calloc(1, sizeof(*ctx)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - ctx->state = CF_SETUP_INIT; - ctx->ssl_mode = ssl_mode; - ctx->transport = transport; - - result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); - if(result) - goto out; - ctx = NULL; - -out: - *pcf = result ? NULL : cf; - if(ctx) { - curlx_free(ctx); - } - return result; -} - -static CURLcode cf_setup_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - uint8_t transport, - int ssl_mode) -{ - struct Curl_cfilter *cf; - CURLcode result = CURLE_OK; - - DEBUGASSERT(data); - result = cf_setup_create(&cf, data, transport, ssl_mode); - if(result) - goto out; - Curl_conn_cf_add(data, conn, sockindex, cf); -out: - return result; -} - -CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data, - uint8_t transport, - int ssl_mode) -{ - struct Curl_cfilter *cf; - CURLcode result; - - DEBUGASSERT(data); - result = cf_setup_create(&cf, data, transport, ssl_mode); - if(result) - goto out; - Curl_conn_cf_insert_after(cf_at, cf); -out: - return result; -} - CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex, - struct Curl_dns_entry *dns, int ssl_mode) { CURLcode result = CURLE_OK; + struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex); uint8_t dns_queries; DEBUGASSERT(data); DEBUGASSERT(conn->scheme); DEBUGASSERT(!conn->cfilter[sockindex]); + if(!peer) + return CURLE_FAILED_INIT; + #ifndef CURL_DISABLE_HTTP if(!conn->cfilter[sockindex] && conn->scheme->protocol == CURLPROTO_HTTPS) { @@ -598,8 +269,8 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, /* Still no cfilter set, apply default. */ if(!conn->cfilter[sockindex]) { - result = cf_setup_add(data, conn, sockindex, - conn->transport_wanted, ssl_mode); + result = Curl_cf_setup_add(data, conn, sockindex, + conn->transport_wanted, ssl_mode); if(result) goto out; } @@ -609,28 +280,179 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, if(sockindex == FIRSTSOCKET) dns_queries |= CURL_DNSQ_HTTPS; #endif - result = Curl_cf_dns_add(data, conn, sockindex, dns_queries, - conn->transport_wanted, dns); + result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, + conn->transport_wanted); DEBUGASSERT(conn->cfilter[sockindex]); out: return result; } -#ifdef USE_UNIX_SOCKETS -const char *Curl_conn_get_unix_path(struct connectdata *conn) +#ifdef CURLVERBOSE +static CURLcode conn_connect_trace(struct Curl_easy *data, + struct Curl_cfilter *cf) { - const char *unix_path = conn->unix_domain_socket; + if(Curl_trc_is_verbose(data)) { + struct ip_quadruple ipquad; + bool is_ipv6; + CURLcode result; -#ifndef CURL_DISABLE_PROXY - if(!unix_path && conn->bits.proxy && 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; + result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad); + if(result) + return result; + + infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ", + (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", + CURL_CONN_HOST_DISPNAME(data->conn), + ipquad.remote_ip, ipquad.remote_port, + ipquad.local_ip, ipquad.local_port); + } + return CURLE_OK; +} #endif - return unix_path; +/** + * Update connection statistics + */ +static void conn_report_connect_stats(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + if(cf) { + struct curltime connected; + struct curltime appconnected; + + memset(&connected, 0, sizeof(connected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); + if(connected.tv_sec || connected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); + + memset(&appconnected, 0, sizeof(appconnected)); + cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); + if(appconnected.tv_sec || appconnected.tv_usec) + Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); + } +} + +CURLcode Curl_conn_connect(struct Curl_easy *data, + int sockindex, + bool blocking, + bool *done) +{ +#define CF_CONN_NUM_POLLS_ON_STACK 5 + struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK]; + struct easy_pollset ps; + struct curl_pollfds cpfds; + struct Curl_cfilter *cf; + CURLcode result = CURLE_OK; + + DEBUGASSERT(data); + DEBUGASSERT(data->conn); + 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 = (bool)cf->connected; + if(*done) + return CURLE_OK; + + Curl_pollset_init(&ps); + Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK); + while(!*done) { + if(Curl_conn_needs_flush(data, sockindex)) { + DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex)); + result = Curl_conn_flush(data, sockindex); + if(result && (result != CURLE_AGAIN)) + goto out; + } + + result = cf->cft->do_connect(cf, data, done); + CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d", + blocking, (int)result, *done); + if(!result && *done) { + /* A final sanity check on connection security */ + if((data->state.origin->scheme->flags & PROTOPT_SSL) && + (sockindex == FIRSTSOCKET) && + !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { + DEBUGASSERT(0); + failf(data, "transfer requires SSL, but not connected via SSL"); + result = CURLE_FAILED_INIT; + goto out; + } + /* Now that the complete filter chain is connected, let all filters + * persist information at the connection. E.g. cf-socket sets the + * socket and ip related information. */ + Curl_conn_cntrl_update_info(data, data->conn); + conn_report_connect_stats(cf, data); + data->conn->keepalive = *Curl_pgrs_now(data); + VERBOSE(result = conn_connect_trace(data, cf)); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected")); + Curl_conn_remove_setup_filters(data, sockindex); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to")); + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", + (int)result); + VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect")); + conn_report_connect_stats(cf, data); + goto out; + } + + if(!blocking) + goto out; + else { + /* check allowed time left */ + const timediff_t timeout_ms = Curl_timeleft_ms(data); + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); + int rc; + + if(timeout_ms < 0) { + /* no need to continue if time already is up */ + failf(data, "connect timeout"); + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + + CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll"); + Curl_pollset_reset(&ps); + Curl_pollfds_reset(&cpfds); + /* In general, we want to send after connect, wait on that. */ + if(sockfd != CURL_SOCKET_BAD) + 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); + if(result) + goto out; + + rc = Curl_poll(cpfds.pfds, cpfds.n, + CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10))); + CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d", + rc); + if(rc < 0) { + result = CURLE_COULDNT_CONNECT; + goto out; + } + /* continue iterating */ + } + } + +out: + Curl_pollset_cleanup(&ps); + Curl_pollfds_cleanup(&cpfds); + return result; } -#endif /* USE_UNIX_SOCKETS */ void Curl_conn_set_multiplex(struct connectdata *conn) { @@ -641,3 +463,32 @@ void Curl_conn_set_multiplex(struct connectdata *conn) } } } + +struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, + int sockindex) +{ + return (sockindex == SECONDARYSOCKET) ? + conn->origin2 : conn->origin; +} + +struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, + int sockindex) +{ + return (sockindex == SECONDARYSOCKET) ? + (conn->via_peer2 ? conn->via_peer2 : conn->origin2) : + (conn->via_peer ? conn->via_peer : conn->origin); +} + +struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, + int sockindex) +{ +#ifndef CURL_DISABLE_PROXY + if(conn->socks_proxy.peer) + return conn->socks_proxy.peer; + if(conn->http_proxy.peer) + return conn->http_proxy.peer; +#endif + return (sockindex == SECONDARYSOCKET) ? + (conn->via_peer2 ? conn->via_peer2 : conn->origin2) : + (conn->via_peer ? conn->via_peer : conn->origin); +} diff --git a/lib/connect.h b/lib/connect.h index 40f1c9c57297..95bde1eb4cf6 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -28,8 +28,7 @@ #include "hostip.h" #include "curlx/timeval.h" -struct Curl_dns_entry; -struct ip_quadruple; +struct Curl_peer; struct Curl_str; enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len); @@ -71,9 +70,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex); curl_socket_t Curl_getconnectinfo(struct Curl_easy *data, struct connectdata **connp); -bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, - char *addr, uint16_t *port); - /* * Curl_conncontrol() marks the end of a connection/stream. The 'ctrl' * argument specifies if it is the end of a connection or a stream. @@ -86,9 +82,9 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, * when the connection will close. */ -#define CONNCTRL_KEEP 0 /* undo a marked closure */ +#define CONNCTRL_KEEP 0 /* undo a marked closure */ #define CONNCTRL_CONNECTION 1 -#define CONNCTRL_STREAM 2 +#define CONNCTRL_STREAM 2 void Curl_conncontrol(struct connectdata *conn, int ctrl @@ -99,42 +95,50 @@ void Curl_conncontrol(struct connectdata *conn, #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) +#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) +#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, - uint8_t transport, - int ssl_mode); - /** * Setup the cfilters at `sockindex` in connection `conn`. * If no filter chain is installed yet, inspects the configuration - * in `data` and `conn? to install a suitable filter chain. + * in `data` and `conn` to install a suitable filter chain. */ CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex, - struct Curl_dns_entry *dns, int ssl_mode); +/** + * Bring the filter chain at `sockindex` for connection `data->conn` into + * connected state. Which will set `*done` to TRUE. + * This can be called on an already connected chain with no side effects. + * When not `blocking`, calls may return without error and `*done != TRUE`, + * while the individual filters negotiated the connection. + */ +CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, + bool blocking, bool *done); + /* 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 +/* Get the origin peer at sockindex. */ +struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, + int sockindex); + +/* Get the peer the connection actually connects to at sockindex. + * Often the same as "origin", but can be redirected via "connect-to" + * or "alt-svc". May tunnel through proxies. */ +struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, + int sockindex); -extern struct Curl_cftype Curl_cft_setup; +/* Get the peer curl connects its socket to. + * Can be origin, "connect-to" or the first proxy. */ +struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, + int sockindex); #endif /* HEADER_CURL_CONNECT_H */ diff --git a/lib/content_encoding.c b/lib/content_encoding.c index 0224a8bfe9e8..fd0807725997 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -46,6 +46,7 @@ #include #endif +#include "connect.h" #include "sendf.h" #include "curl_trc.h" #include "content_encoding.h" @@ -88,7 +89,7 @@ static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size) { (void)opaque; /* not a typo, keep it curlx_calloc() */ - return (voidpf)curlx_calloc(items, size); + return curlx_calloc(items, size); } static void zfree_cb(voidpf opaque, voidpf ptr) @@ -100,8 +101,7 @@ static void zfree_cb(voidpf opaque, voidpf ptr) static CURLcode process_zlib_error(struct Curl_easy *data, z_stream *z) { if(z->msg) - failf(data, "Error while processing content unencoding: %s", - z->msg); + failf(data, "Error while processing content unencoding: %s", z->msg); else failf(data, "Error while processing content unencoding: " "Unknown failure within decompression software."); @@ -154,6 +154,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, z_const Bytef *orig_in = z->next_in; bool done = FALSE; CURLcode result = CURLE_OK; /* Curl_client_write status */ + int i = 0; /* Check state. */ if(zp->zlib_init != ZLIB_INIT && @@ -164,9 +165,18 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* because the buffer size is fixed, iteratively decompress and transfer to the client via next_write function. */ while(!done) { - int status; /* zlib status */ + int status; /* zlib status */ done = TRUE; + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return exit_zlib(data, z, &zp->zlib_init, CURLE_OPERATION_TIMEDOUT); + } + } + /* (re)set buffer for decompressed output for every iteration */ z->next_out = (Bytef *)zp->buffer; z->avail_out = DECOMPRESS_BUFFER_SIZE; @@ -176,7 +186,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, /* Flush output data if some. */ if(z->avail_out != DECOMPRESS_BUFFER_SIZE) { if(status == Z_OK || status == Z_STREAM_END) { - zp->zlib_init = started; /* Data started. */ + zp->zlib_init = started; /* Data started. */ result = Curl_cwriter_write(data, writer->next, type, zp->buffer, DECOMPRESS_BUFFER_SIZE - z->avail_out); if(result) { @@ -210,7 +220,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, done = FALSE; break; } - zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ + zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */ } result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z)); break; @@ -224,7 +234,7 @@ static CURLcode inflate_stream(struct Curl_easy *data, again. If we are in a state that would wrongly allow restart in raw mode at the next call, assume output has already started. */ if(nread && zp->zlib_init == ZLIB_INIT) - zp->zlib_init = started; /* Cannot restart anymore. */ + zp->zlib_init = started; /* Cannot restart anymore. */ return result; } @@ -234,7 +244,7 @@ static CURLcode deflate_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ /* Initialize zlib */ z->zalloc = (alloc_func)zalloc_cb; @@ -251,7 +261,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -271,7 +281,7 @@ static void deflate_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } @@ -293,7 +303,7 @@ static CURLcode gzip_do_init(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ /* Initialize zlib */ z->zalloc = (alloc_func)zalloc_cb; @@ -311,7 +321,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data, const char *buf, size_t nbytes) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -332,7 +342,7 @@ static void gzip_do_close(struct Curl_easy *data, struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *)writer; - z_stream *z = &zp->z; /* zlib state structure */ + z_stream *z = &zp->z; /* zlib state structure */ exit_zlib(data, z, &zp->zlib_init, CURLE_OK); } @@ -353,7 +363,7 @@ static const struct Curl_cwtype gzip_encoding = { struct brotli_writer { struct Curl_cwriter super; char buffer[DECOMPRESS_BUFFER_SIZE]; - BrotliDecoderState *br; /* State structure for brotli. */ + BrotliDecoderState *br; /* State structure for brotli. */ }; static CURLcode brotli_map_error(BrotliDecoderErrorCode be) @@ -412,15 +422,26 @@ static CURLcode brotli_do_write(struct Curl_easy *data, size_t dstleft; CURLcode result = CURLE_OK; BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT; + int i = 0; if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); if(!bp->br) - return CURLE_WRITE_ERROR; /* Stream already ended. */ + return CURLE_WRITE_ERROR; /* Stream already ended. */ while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) && result == CURLE_OK) { + + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return CURLE_OPERATION_TIMEDOUT; + } + } + dst = (uint8_t *)bp->buffer; dstleft = DECOMPRESS_BUFFER_SIZE; r = BrotliDecoderDecompressStream(bp->br, @@ -473,7 +494,7 @@ static const struct Curl_cwtype brotli_encoding = { /* Zstd writer. */ struct zstd_writer { struct Curl_cwriter super; - ZSTD_DStream *zds; /* State structure for zstd. */ + ZSTD_DStream *zds; /* State structure for zstd. */ char buffer[DECOMPRESS_BUFFER_SIZE]; }; @@ -520,6 +541,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, ZSTD_inBuffer in; ZSTD_outBuffer out; size_t errorCode; + int i = 0; if(!(type & CLIENTWRITE_BODY) || !nbytes) return Curl_cwriter_write(data, writer->next, type, buf, nbytes); @@ -529,6 +551,15 @@ static CURLcode zstd_do_write(struct Curl_easy *data, in.size = nbytes; for(;;) { + if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) { + /* check every MB of output if we are not exceeding time limit */ + i = 0; + if(Curl_timeleft_ms(data) < 0) { + failf(data, "Operation timed out while decoding payload"); + return CURLE_OPERATION_TIMEDOUT; + } + } + out.pos = 0; out.dst = zp->buffer; out.size = DECOMPRESS_BUFFER_SIZE; @@ -750,24 +781,14 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, return CURLE_OK; } - if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to more than %d content encodings", - MAX_ENCODE_STACK); + if(Curl_cwriter_count(data, phase) >= MAX_ENCODE_STACK) { + failf(data, "Reject response exceeding limit of %d %s encodings", + MAX_ENCODE_STACK, + is_transfer ? "transfer" : "content"); return CURLE_BAD_CONTENT_ENCODING; } cwt = find_unencode_writer(name, namelen, phase); - if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) { - /* A 'chunked' transfer encoding has already been added. - * Ignore duplicates. See #13451. - * Also RFC 9112, ch. 6.1: - * "A sender MUST NOT apply the chunked transfer coding more than - * once to a message body." - */ - CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder"); - return CURLE_OK; - } - if(is_transfer && !is_chunked && Curl_cwriter_get_by_name(data, "chunked")) { /* RFC 9112, ch. 6.1: @@ -782,20 +803,31 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, "Transfer-Encoding"); return CURLE_BAD_CONTENT_ENCODING; } - - if(!cwt) - cwt = &error_writer; /* Defer error at use. */ - - result = Curl_cwriter_create(&writer, data, cwt, phase); - CURL_TRC_WRITE(data, "added %s decoder %s -> %d", - is_transfer ? "transfer" : "content", cwt->name, result); - if(result) - return result; - - result = Curl_cwriter_add(data, writer); - if(result) { - Curl_cwriter_free(data, writer); - return result; + if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) { + /* A 'chunked' transfer encoding has already been added. + * Ignore duplicates. See #13451. + * Also RFC 9112, ch. 6.1: + * "A sender MUST NOT apply the chunked transfer coding more than + * once to a message body." + */ + CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder"); + } + else { + if(!cwt) + cwt = &error_writer; /* Defer error at use. */ + + result = Curl_cwriter_create(&writer, data, cwt, phase); + CURL_TRC_WRITE(data, "added %s decoder %s -> %d", + is_transfer ? "transfer" : "content", cwt->name, + (int)result); + if(result) + return result; + + result = Curl_cwriter_add(data, writer); + if(result) { + Curl_cwriter_free(data, writer); + return result; + } } if(is_chunked) has_chunked = TRUE; diff --git a/lib/cookie.c b/lib/cookie.c index 0f822ea7cf20..91dc8d5fd0d2 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -29,6 +29,7 @@ #include "cookie.h" #include "psl.h" #include "curl_trc.h" +#include "transfer.h" #include "slist.h" #include "curl_share.h" #include "strcase.h" @@ -71,7 +72,7 @@ static void freecookie(struct Cookie *co, bool maintoo) } static bool cookie_tailmatch(const char *cookie_domain, - size_t cookie_domain_len, + const size_t cookie_domain_len, const char *hostname) { size_t hostname_len = strlen(hostname); @@ -250,19 +251,11 @@ static char *sanitize_cookie_path(const char *cookie_path, size_t len) /* * strstore * - * A thin wrapper around strdup which ensures that any memory allocated at - * *str will be freed before the string allocated by strdup is stored there. - * The intended usecase is repeated assignments to the same variable during - * parsing in a last-wins scenario. The caller is responsible for checking - * for OOM errors. + * A thin wrapper around curlx_memdup0(). */ static CURLcode strstore(char **str, const char *newstr, size_t len) { DEBUGASSERT(str); - if(!len) { - len++; - newstr = ""; - } *str = curlx_memdup0(newstr, len); if(!*str) return CURLE_OUT_OF_MEMORY; @@ -354,9 +347,9 @@ static bool bad_domain(const char *domain, size_t len) 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 */ + /* Reject all bytes \x01 - \x1f + \x7f */ while(len && *p) { - if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f)) + if((*p < 0x20) || (*p == 0x7f)) return TRUE; p++; len--; @@ -377,7 +370,7 @@ static bool invalid_octets(const char *ptr, size_t len) #define COOKIE_PIECES 4 /* the list above */ -static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, +static CURLcode storecookie(struct Cookie *co, const struct Curl_str *cp, const char *path, const char *domain) { CURLcode result; @@ -421,19 +414,189 @@ static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp, 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 */ +/* + * Parse the first name/value pair of the cookie header, which is the actual + * cookie name and value. + */ +static bool parse_first_pair(struct Curl_easy *data, struct Cookie *co, + struct Curl_str *cookie, + struct Curl_str *name, + struct Curl_str *val, + bool sep) +{ + /* The first name/value pair is the actual cookie name */ + if(!sep || !curlx_strlen(name)) { + infof(data, "invalid cookie, dropped"); + return FALSE; + } + + /* + * Check for too long individual name or contents. Chrome and Firefox + * support 4095 or 4096 bytes combo + */ + if((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 FALSE; + } + + /* 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; + return TRUE; +} + +static bool parse_flag(struct Curl_easy *data, struct Cookie *co, + const struct CookieInfo *ci, + struct Curl_str *name, bool secure) +{ + /* + * 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) + co->secure = TRUE; + else { + infof(data, "skipped cookie because not 'secure'"); + return FALSE; + } + } + else if(curlx_str_casecompare(name, "httponly")) + co->httponly = TRUE; + + return TRUE; +} + +static bool parse_domain(struct Curl_easy *data, struct Cookie *co, + struct Curl_str *cookie_domain, + struct Curl_str *val, + const char **domainp) +{ + bool is_ip; + const char *domain = *domainp; + const char *v = curlx_str(val); + /* + * Now, we make sure that our host is within the given domain, or + * the given domain is not valid and thus cannot be set. + */ + + if('.' == *v) + curlx_str_nudge(val, 1); + +#ifndef USE_LIBPSL + /* + * Without PSL we do not know when the incoming cookie is set on a + * TLD or otherwise "protected" suffix. To reduce risk, we require a + * dot OR the exact hostname being "localhost". + */ + if(bad_domain(curlx_str(val), curlx_strlen(val))) { + *domainp = ":"; + domain = ":"; + } +#endif + + 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), + curlx_strlen(val), domain))) { + *cookie_domain = *val; + if(!is_ip) + co->tailmatch = TRUE; /* we always do that if the domain name was + given */ + } + else { + /* + * We did not get a tailmatch and then the attempted set domain is + * not a domain to which the current host belongs. Mark as bad. + */ + infof(data, "skipped cookie with bad tailmatch domain: %s", + curlx_str(val)); + return FALSE; + } + return TRUE; +} + +static void parse_maxage(struct Cookie *co, struct Curl_str *val, + time_t *nowp) +{ + int rc; + const char *maxage = curlx_str(val); + if(*maxage == '\"') + maxage++; + rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX); + if(!*nowp) + *nowp = time(NULL); + switch(rc) { + case STRE_OVERFLOW: + /* overflow, used max value */ + co->expires = CURL_OFF_T_MAX; + break; + default: + /* negative or otherwise bad, expire */ + co->expires = 1; + break; + case STRE_OK: + if(!co->expires) + co->expires = 1; /* expire now */ + else if(CURL_OFF_T_MAX - *nowp < co->expires) + /* would overflow */ + co->expires = CURL_OFF_T_MAX; + else + co->expires += *nowp; + break; + } + cap_expires(*nowp, co); +} + +static void parse_expires(struct Cookie *co, struct Curl_str *val, + time_t *nowp) +{ + /* + * Let max-age have priority. + * + * If the date cannot get parsed for whatever reason, the cookie + * will be treated as a session cookie + */ + if(!co->expires && (curlx_strlen(val) < MAX_DATE_LENGTH)) { + 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(!*nowp) + *nowp = time(NULL); + cap_expires(*nowp, co); + } +} + +/* this function returns errors on OOM etc, not for cookie format problems */ +static CURLcode +parse_cookie_header(struct Curl_easy *data, + struct Cookie *co, + const struct CookieInfo *ci, + bool *okay, /* if the cookie was fine */ + const char *ptr, /* the header */ + const char *domain, /* default domain */ + /* full path used when this cookie is set */ + const char *path, + bool secure_origin) { /* This line was read off an HTTP-header */ time_t now = 0; @@ -449,22 +612,25 @@ static CURLcode parse_cookie_header( 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=")) { + struct Curl_str val; bool sep = FALSE; curlx_str_trimblanks(&name); + if(invalid_octets(curlx_str(&name), curlx_strlen(&name))) { + infof(data, "invalid octets in name, cookie dropped"); + return CURLE_OK; + } + if(!curlx_str_single(&ptr, '=')) { sep = TRUE; /* a '=' was used */ if(!curlx_str_cspn(&ptr, &val, ";\r\n")) curlx_str_trimblanks(&val); - /* 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"); + if(invalid_octets(curlx_str(&val), curlx_strlen(&val))) { + infof(data, "invalid octets in value, cookie dropped"); return CURLE_OK; } } @@ -472,167 +638,23 @@ static CURLcode parse_cookie_header( curlx_str_init(&val); 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"); + if(!parse_first_pair(data, co, cookie, &name, &val, sep)) return CURLE_OK; - } - - /* - * 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 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) - co->secure = TRUE; - else { - infof(data, "skipped cookie because not 'secure'"); - return CURLE_OK; - } - } - else if(curlx_str_casecompare(&name, "httponly")) - co->httponly = TRUE; + if(!parse_flag(data, co, ci, &name, secure_origin)) + return CURLE_OK; } - else if(curlx_str_casecompare(&name, "path")) { + else if(curlx_str_casecompare(&name, "path")) cookie[COOKIE_PATH] = val; - } else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) { - bool is_ip; - const char *v = curlx_str(&val); - /* - * Now, we make sure that our host is within the given domain, or - * the given domain is not valid and thus cannot be set. - */ - - if('.' == *v) - curlx_str_nudge(&val, 1); - -#ifndef USE_LIBPSL - /* - * Without PSL we do not know when the incoming cookie is set on a - * TLD or otherwise "protected" suffix. To reduce risk, we require a - * dot OR the exact hostname being "localhost". - */ - if(bad_domain(curlx_str(&val), curlx_strlen(&val))) - domain = ":"; -#endif - - 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), - curlx_strlen(&val), domain))) { - cookie[COOKIE_DOMAIN] = val; - if(!is_ip) - co->tailmatch = TRUE; /* we always do that if the domain name was - given */ - } - else { - /* - * We did not get a tailmatch and then the attempted set domain is - * not a domain to which the current host belongs. Mark as bad. - */ - infof(data, "skipped cookie with bad tailmatch domain: %s", - curlx_str(&val)); + if(!parse_domain(data, co, &cookie[COOKIE_DOMAIN], &val, &domain)) return CURLE_OK; - } - } - else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) { - /* - * Defined in RFC2109: - * - * Optional. The Max-Age attribute defines the lifetime of the - * cookie, in seconds. The delta-seconds value is a decimal non- - * negative integer. After delta-seconds seconds elapse, the - * client should discard the cookie. A value of zero means the - * cookie should be discarded immediately. - */ - int rc; - const char *maxage = curlx_str(&val); - 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 */ - co->expires = CURL_OFF_T_MAX; - break; - default: - /* negative or otherwise bad, expire */ - co->expires = 1; - break; - case STRE_OK: - if(!co->expires) - co->expires = 1; /* expire now */ - else if(CURL_OFF_T_MAX - now < co->expires) - /* would overflow */ - co->expires = CURL_OFF_T_MAX; - else - co->expires += now; - break; - } - 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 if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) + parse_maxage(co, &val, &now); + else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val)) + parse_expires(co, &val, &now); } } while(!curlx_str_single(&ptr, ';')); @@ -646,11 +668,10 @@ static CURLcode parse_cookie_header( } static CURLcode parse_netscape(struct Cookie *co, - struct CookieInfo *ci, + const struct CookieInfo *ci, bool *okay, const char *lineptr, - bool secure) /* TRUE if connection is over - secure origin */ + bool secure_origin) { /* * This line is NOT an HTTP header style line, we do offer support for @@ -667,7 +688,7 @@ static CURLcode parse_netscape(struct Cookie *co, * Firefox's cookie files, they are prefixed #HttpOnly_ and the rest * remains as usual, so we skip 10 characters of the line. */ - if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { + if(!strncmp(lineptr, "#HttpOnly_", 10)) { lineptr += 10; co->httponly = TRUE; } @@ -723,7 +744,7 @@ static CURLcode parse_netscape(struct Cookie *co, case 3: co->secure = FALSE; if(curl_strnequal(ptr, "TRUE", len)) { - if(secure || ci->running) + if(secure_origin || ci->running) co->secure = TRUE; else return CURLE_OK; @@ -738,10 +759,12 @@ static CURLcode parse_netscape(struct Cookie *co, if(!co->name) return CURLE_OUT_OF_MEMORY; else { - /* For Netscape file format cookies we check prefix on the name */ - if(curl_strnequal("__Secure-", co->name, 9)) + /* For Netscape file format cookies we check prefix on the name. + These prefixes are matched case sensitively, same as on the + header path and as the 6265bis document specifies. */ + if(!strncmp("__Secure-", co->name, 9)) co->prefix_secure = TRUE; - else if(curl_strnequal("__Host-", co->name, 7)) + else if(!strncmp("__Host-", co->name, 7)) co->prefix_host = TRUE; } break; @@ -765,12 +788,20 @@ static CURLcode parse_netscape(struct Cookie *co, /* we did not find the sufficient number of fields */ return CURLE_OK; + /* Reject control octets in the name or value, matching the filtering done + for cookies set over HTTP. A cookie loaded from a file is later sent in + request headers, so the same bytes that make a server reject a request + must not slip in through the file. */ + if(invalid_octets(co->name, strlen(co->name)) || + invalid_octets(co->value, strlen(co->value))) + return CURLE_OK; + *okay = TRUE; return CURLE_OK; } static bool is_public_suffix(struct Curl_easy *data, - struct Cookie *co, + const struct Cookie *co, const char *domain) { #ifdef USE_LIBPSL @@ -787,12 +818,21 @@ static bool is_public_suffix(struct Curl_easy *data, char lcookie[256]; size_t dlen = strlen(domain); size_t clen = strlen(co->domain); + + /* trim trailing dots */ + if(dlen && (domain[dlen - 1] == '.')) + dlen--; + if(clen && (co->domain[clen - 1] == '.')) + clen--; + if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { const psl_ctx_t *psl = Curl_psl_use(data); if(psl) { /* the PSL check requires lowercase domain name and pattern */ - Curl_strntolower(lcase, domain, dlen + 1); - Curl_strntolower(lcookie, co->domain, clen + 1); + Curl_strntolower(lcase, domain, dlen); + lcase[dlen] = 0; + Curl_strntolower(lcookie, co->domain, clen); + lcookie[clen] = 0; acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); Curl_psl_release(data); } @@ -819,7 +859,7 @@ static bool is_public_suffix(struct Curl_easy *data, /* returns TRUE when replaced */ static bool replace_existing(struct Curl_easy *data, struct Cookie *co, - struct CookieInfo *ci, + const struct CookieInfo *ci, bool secure, bool *replacep) { @@ -834,8 +874,10 @@ static bool replace_existing(struct Curl_easy *data, bool matching_domains = FALSE; if(clist->domain && co->domain) { - if(curl_strequal(clist->domain, co->domain)) - /* The domains are identical */ + if(cookie_tailmatch(clist->domain, strlen(clist->domain), + co->domain) || + cookie_tailmatch(co->domain, strlen(co->domain), clist->domain)) + /* The existing one is a tail of the new or vice versa */ matching_domains = TRUE; } else if(!clist->domain && !co->domain) @@ -862,7 +904,7 @@ static bool replace_existing(struct Curl_easy *data, else cllen = strlen(clist->path); - if(curl_strnequal(clist->path, co->path, cllen)) { + if(!strncmp(clist->path, co->path, cllen)) { infof(data, "cookie '%s' for domain '%s' dropped, would " "overlay an existing cookie", co->name, co->domain); return FALSE; @@ -886,7 +928,7 @@ static bool replace_existing(struct Curl_easy *data, /* the domains were identical */ if(clist->path && co->path && - !curl_strequal(clist->path, co->path)) + strcmp(clist->path, co->path)) replace_old = FALSE; else if(!clist->path != !co->path) replace_old = FALSE; @@ -1152,7 +1194,7 @@ static CURLcode cookie_load(struct Curl_easy *data, const char *file, curlx_fclose(handle); } data->state.cookie_engine = TRUE; - ci->running = TRUE; /* now, we are running */ + ci->running = TRUE; /* now, we are running */ return result; } @@ -1237,9 +1279,9 @@ static int cookie_sort_ct(const void *p1, const void *p2) return (c2->creationtime > c1->creationtime) ? 1 : -1; } -bool Curl_secure_context(struct connectdata *conn, const char *host) +bool Curl_secure_context(struct Curl_easy *data, const char *host) { - return conn->scheme->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS) || + return Curl_xfer_is_secure(data) || curl_strequal("localhost", host) || !strcmp(host, "127.0.0.1") || !strcmp(host, "::1"); @@ -1249,15 +1291,13 @@ bool Curl_secure_context(struct connectdata *conn, const char *host) * 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. + * should send to the server if used now. * * It shall only return cookies that have not expired. * * 'okay' is TRUE when there is a list returned. */ CURLcode Curl_cookie_getlist(struct Curl_easy *data, - struct connectdata *conn, bool *okay, const char *host, struct Curl_llist *list) @@ -1266,7 +1306,7 @@ CURLcode Curl_cookie_getlist(struct Curl_easy *data, const bool is_ip = Curl_host_is_ipnum(host); const size_t myhash = cookiehash(host); struct Curl_llist_node *n; - const bool secure = Curl_secure_context(conn, host); + const bool secure = Curl_secure_context(data, host); struct CookieInfo *ci = data->cookies; const char *path = data->state.up.path; CURLcode result = CURLE_OK; @@ -1561,7 +1601,7 @@ static CURLcode cookie_output(struct Curl_easy *data, return result; } -static struct curl_slist *cookie_list(struct Curl_easy *data) +static struct curl_slist *cookie_list(const struct Curl_easy *data) { struct curl_slist *list = NULL; struct curl_slist *beg; diff --git a/lib/cookie.h b/lib/cookie.h index f66e0ef59182..b8ef8b8ea152 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -109,7 +109,7 @@ struct connectdata; * are only used if the header boolean is TRUE. */ -bool Curl_secure_context(struct connectdata *conn, const char *host); +bool Curl_secure_context(struct Curl_easy *data, const char *host); CURLcode Curl_cookie_add(struct Curl_easy *data, struct CookieInfo *ci, bool httpheader, @@ -118,7 +118,7 @@ CURLcode Curl_cookie_add(struct Curl_easy *data, const char *domain, const char *path, bool secure) WARN_UNUSED_RESULT; -CURLcode Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn, +CURLcode Curl_cookie_getlist(struct Curl_easy *data, bool *okay, const char *host, struct Curl_llist *list) WARN_UNUSED_RESULT; void Curl_cookie_clearall(struct CookieInfo *ci); diff --git a/lib/creds.c b/lib/creds.c new file mode 100644 index 000000000000..d22c166a4275 --- /dev/null +++ b/lib/creds.c @@ -0,0 +1,189 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 /* for offsetof() */ + +#include "creds.h" +#include "curl_trc.h" +#include "strcase.h" +#include "urldata.h" + + +CURLcode Curl_creds_create(const char *user, + const char *passwd, + const char *oauth_bearer, + const char *sasl_authzid, + const char *sasl_service, + uint8_t source, + struct Curl_creds **pcreds) +{ + struct Curl_creds *creds = NULL; + size_t ulen = user ? strlen(user) : 0; + size_t plen = passwd ? strlen(passwd) : 0; + size_t olen = oauth_bearer ? strlen(oauth_bearer) : 0; + size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0; + size_t sslen = sasl_service ? strlen(sasl_service) : 0; + char *s, *buf; + CURLcode result = CURLE_OK; + + Curl_creds_unlink(pcreds); + + /* Everything empty/NULL, this is the NULL credential */ + if(!user && !passwd && !olen && !salen && !sslen) + goto out; + + if((ulen > CURL_MAX_INPUT_LENGTH) || + (plen > CURL_MAX_INPUT_LENGTH) || + (olen > CURL_MAX_INPUT_LENGTH) || + (salen > CURL_MAX_INPUT_LENGTH) || + (sslen > CURL_MAX_INPUT_LENGTH)) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + + /* null-terminator for user already part of struct */ + creds = curlx_calloc(1, sizeof(*creds) + + ulen + plen + 1 + olen + 1 + salen + 1 + sslen + 1); + if(!creds) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + creds->refcount = 1; + creds->source = source; + /* Some compilers try to be too smart about our dynamic struct size */ + buf = ((char *)creds) + offsetof(struct Curl_creds, buf); + creds->user = s = buf; + if(ulen) + memcpy(s, user, ulen + 1); + creds->passwd = s = buf + ulen + 1; + if(plen) + memcpy(s, passwd, plen + 1); + creds->oauth_bearer = s = buf + ulen + 1 + plen + 1; + if(olen) + memcpy(s, oauth_bearer, olen + 1); + creds->sasl_authzid = s = buf + ulen + 1 + plen + 1 + olen + 1; + if(salen) + memcpy(s, sasl_authzid, salen + 1); + creds->sasl_service = s = buf + ulen + 1 + plen + 1 + olen + 1 + salen + 1; + if(sslen) + memcpy(s, sasl_service, sslen + 1); + +out: + if(!result) + *pcreds = creds; + else + Curl_creds_unlink(&creds); + return result; +} + +CURLcode Curl_creds_merge(const char *user, + const char *passwd, + struct Curl_creds *creds_in, + uint8_t source, + struct Curl_creds **pcreds_out) +{ + struct Curl_creds *creds_out = NULL; + CURLcode result; + + if(!creds_in) { + result = Curl_creds_create(user, passwd, NULL, NULL, NULL, + source, &creds_out); + } + else { + result = Curl_creds_create(user ? user : Curl_creds_user(creds_in), + passwd ? passwd : Curl_creds_passwd(creds_in), + Curl_creds_oauth_bearer(creds_in), + Curl_creds_sasl_authzid(creds_in), + Curl_creds_sasl_service(creds_in), + source, &creds_out); + } + Curl_creds_link(pcreds_out, creds_out); + Curl_creds_unlink(&creds_out); + return result; +} + +void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src) +{ + if(*pdest != src) { + Curl_creds_unlink(pdest); + *pdest = src; + if(src) { + DEBUGASSERT(src->refcount < UINT32_MAX); + src->refcount++; + } + } +} + +void Curl_creds_unlink(struct Curl_creds **pcreds) +{ + if(*pcreds) { + struct Curl_creds *creds = *pcreds; + + DEBUGASSERT(creds->refcount); + *pcreds = NULL; + if(creds->refcount) + creds->refcount--; + if(!creds->refcount) { + curlx_free(creds); + } + } +} + +bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2) +{ + return (c1 == c2) || + (c1 && c2 && + !Curl_timestrcmp(c1->user, c2->user) && + !Curl_timestrcmp(c1->passwd, c2->passwd) && + !Curl_timestrcmp(c1->oauth_bearer, c2->oauth_bearer) && + !Curl_timestrcmp(c1->sasl_authzid, c2->sasl_authzid) && + !Curl_timestrcmp(c1->sasl_service, c2->sasl_service)); +} + +bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2) +{ + return Curl_creds_same(c1, c2) && + ((c1 == c2) || (c1 && c2 && (c1->source == c2->source))); +} + +#ifdef CURLVERBOSE +void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, + const char *msg) +{ + if(creds) { + CURL_TRC_M(data, "%s: user=%s, passwd=%s, " + "sasl_authzid=%s, oauth_bearer=%s, source=%d", + msg, + Curl_creds_user(creds), + Curl_creds_has_passwd(creds) ? "***" : "", + Curl_creds_sasl_authzid(creds), + Curl_creds_has_oauth_bearer(creds) ? "***" : "", + creds->source); + } + else + CURL_TRC_M(data, "%s: -", msg); +} +#endif diff --git a/lib/creds.h b/lib/creds.h new file mode 100644 index 000000000000..0664d08a4f4f --- /dev/null +++ b/lib/creds.h @@ -0,0 +1,91 @@ +#ifndef HEADER_CURL_CREDS_H +#define HEADER_CURL_CREDS_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_easy; + +#define CREDS_NONE 0 /* used for default username/passwd */ +#define CREDS_URL 1 /* username/passwd from URL */ +#define CREDS_OPTION 2 /* username/passwd set with a CURLOPT_ */ +#define CREDS_NETRC 3 /* username/passwd found in netrc */ + +struct Curl_creds { + const char *user; /* non-NULL, maybe empty string */ + const char *passwd; /* non-NULL, maybe empty string */ + const char *oauth_bearer; /* non-NULL, maybe empty string */ + const char *sasl_authzid; /* non-NULL, maybe empty string */ + const char *sasl_service; /* non-NULL, maybe empty string */ + uint32_t refcount; + uint8_t source; /* CREDS_* value */ + char buf[1]; +}; + +CURLcode Curl_creds_create(const char *user, + const char *passwd, + const char *oauth_bearer, + const char *sasl_authzid, + const char *sasl_service, + uint8_t source, + struct Curl_creds **pcreds); + +/* Create credentials by overriding `user` and/or `passwd` in `creds_in` */ +CURLcode Curl_creds_merge(const char *user, + const char *passwd, + struct Curl_creds *creds_in, + uint8_t source, + struct Curl_creds **pcreds_out); + +/* Unlink any creds in `*pdest`, assign src, increase src + * refcount when not NULL. */ +void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src); + +/* Drop a reference, creds may be passed as NULL */ +void Curl_creds_unlink(struct Curl_creds **pcreds); + +/* TRUE if both creds are NULL or have same values, except source. */ +bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2); + +/* TRUE if both creds are NULL or have all values equal. */ +bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2); + +/* Provides properties for creds or, if creds is NULL, the empty string */ +#define Curl_creds_has_user(c) ((c) && (c)->user[0]) +#define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0]) +#define Curl_creds_has_user_or_pass(c) \ + ((c) && ((c)->user[0] || (c)->passwd[0])) +#define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0]) +#define Curl_creds_has_sasl_service(c) ((c) && (c)->sasl_service[0]) +#define Curl_creds_user(c) ((c) ? (c)->user : "") +#define Curl_creds_passwd(c) ((c) ? (c)->passwd : "") +#define Curl_creds_oauth_bearer(c) ((c) ? (c)->oauth_bearer : "") +#define Curl_creds_sasl_authzid(c) ((c) ? (c)->sasl_authzid : "") +#define Curl_creds_sasl_service(c) ((c) ? (c)->sasl_service : "") + +#ifdef CURLVERBOSE +void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds, + const char *msg); +#endif + +#endif /* HEADER_CURL_CREDS_H */ diff --git a/lib/cshutdn.c b/lib/cshutdn.c index 27b4a9f0dd31..cc77e401fd32 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -150,8 +150,8 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T, conn->bits.shutdown_filters ? "" : "force ", conn->connection_id); - Curl_conn_close(admin, SECONDARYSOCKET); - Curl_conn_close(admin, FIRSTSOCKET); + Curl_conn_cf_discard_all(admin, conn, SECONDARYSOCKET); + Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET); Curl_detach_connection(admin); if(data->multi) @@ -322,14 +322,14 @@ int Curl_cshutdn_init(struct cshutdn *cshutdn, DEBUGASSERT(multi); cshutdn->multi = multi; Curl_llist_init(&cshutdn->list, NULL); - cshutdn->initialised = TRUE; + cshutdn->initialized = TRUE; return 0; /* good */ } void Curl_cshutdn_destroy(struct cshutdn *cshutdn, struct Curl_easy *data) { - if(cshutdn->initialised && data) { + if(cshutdn->initialized && data) { int timeout_ms = 0; /* for testing, run graceful shutdown */ #ifdef DEBUGBUILD diff --git a/lib/cshutdn.h b/lib/cshutdn.h index b2e83f3d1aad..8479524993cf 100644 --- a/lib/cshutdn.h +++ b/lib/cshutdn.h @@ -54,7 +54,7 @@ void Curl_cshutdn_terminate(struct Curl_easy *data, struct cshutdn { struct Curl_llist list; /* connections being shut down */ struct Curl_multi *multi; /* the multi owning this */ - BIT(initialised); + BIT(initialized); }; /* Init as part of the given multi handle. */ diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index 4cd1c4de848c..9699623e6d43 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -58,6 +58,7 @@ #include "curl_addrinfo.h" #include "fake_addrinfo.h" #include "curlx/inet_pton.h" +#include "curlx/strparse.h" /* * Curl_freeaddrinfo() @@ -68,7 +69,7 @@ * any function call which actually allocates a Curl_addrinfo struct. */ -#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ +#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \ defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__) /* workaround icc 9.1 optimizer issue */ # define vqualifier volatile @@ -122,7 +123,7 @@ int Curl_getaddrinfo_ex(const char *nodename, /* traverse the addrinfo list */ - for(ai = aihead; ai != NULL; ai = ai->ai_next) { + for(ai = aihead; ai; 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. */ @@ -265,7 +266,7 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port) /* no input == no output! */ return NULL; - DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL)); + DEBUGASSERT(he->h_name && he->h_addr_list); for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) { size_t ss_size; @@ -452,6 +453,48 @@ bool Curl_is_ipaddr(const char *address) return FALSE; } +bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, + struct Curl_str *host, struct Curl_str *zone) +{ + const char *zonep = NULL; + size_t i = 0, hlen = 0, zlen = 0; + + if(host) + memset(host, 0, sizeof(*host)); + if(zone) + memset(zone, 0, sizeof(*zone)); + + for(i = 0; i < len; ++i, ++hlen) { + if(!s[i] || !(ISXDIGIT(s[i]) || (s[i] == ':') || (s[i] == '.'))) + break; + } + + if((i < len) && (s[i] == '%')) { /* address followed by a zone? */ + i += 1; + if(maybe_url_encoded && !strncmp("25", s + i, 2)) + i += 2; + zonep = s + i; + for(; i < len; ++i, ++zlen) { + /* Allow unreserved characters as defined in RFC 3986 */ + if(!s[i] || !(ISALPHA(s[i]) || ISXDIGIT(s[i]) || (s[i] == '-') || + (s[i] == '.') || (s[i] == '_') || (s[i] == '~'))) + break; + } + } + + if(i != len) + return FALSE; /* invalid chars in zone */ + if(host && hlen) { + host->str = s; + host->len = hlen; + } + if(zone && zlen) { + zone->str = zonep; + zone->len = zlen; + } + return TRUE; +} + #ifdef USE_UNIX_SOCKETS /** * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo @@ -569,7 +612,7 @@ int curl_dbg_getaddrinfo(const char *hostname, #if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS) /* - * Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and macOS + * Works around the sin6_port is always zero bug on iOS 9.3.2 and macOS * 10.11.5. */ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) @@ -579,7 +622,7 @@ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port) #ifdef USE_IPV6 struct sockaddr_in6 *addr6; #endif - for(ca = addrinfo; ca != NULL; ca = ca->ai_next) { + for(ca = addrinfo; ca; ca = ca->ai_next) { switch(ca->ai_family) { case AF_INET: addr = (void *)ca->ai_addr; /* storage area for this info */ diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h index da2da872cfcc..046be238169d 100644 --- a/lib/curl_addrinfo.h +++ b/lib/curl_addrinfo.h @@ -40,6 +40,8 @@ # include #endif +struct Curl_str; + /* * Curl_addrinfo is our internal struct definition that we use to allow * consistent internal handling of this data. We use this even when the system @@ -73,6 +75,9 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port); bool Curl_is_ipv4addr(const char *address); bool Curl_is_ipaddr(const char *address); +bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded, + struct Curl_str *host, struct Curl_str *zone); + CURLcode Curl_str2addr(const char *dotted, uint16_t port, struct Curl_addrinfo **addrp); diff --git a/lib/curl_config-cmake.h.in b/lib/curl_config-cmake.h.in index f4b9f4f45806..d18ec60caa5e 100644 --- a/lib/curl_config-cmake.h.in +++ b/lib/curl_config-cmake.h.in @@ -22,13 +22,13 @@ * ***************************************************************************/ -/* Location of default ca bundle */ +/* Location of default CA bundle */ #cmakedefine CURL_CA_BUNDLE "${CURL_CA_BUNDLE}" -/* define "1" to use built-in ca store of TLS backend */ +/* define "1" to use built-in CA store of TLS backend */ #cmakedefine CURL_CA_FALLBACK 1 -/* Location of default ca path */ +/* Location of default CA path */ #cmakedefine CURL_CA_PATH "${CURL_CA_PATH}" /* Default SSL backend */ @@ -234,6 +234,12 @@ /* Define to 1 if you have the `opendir' function. */ #cmakedefine HAVE_OPENDIR 1 +/* Define to 1 if you have the memset_explicit (C23) function. */ +#cmakedefine HAVE_MEMSET_EXPLICIT 1 + +/* Define to 1 if you have the memset_s (C11) function. */ +#cmakedefine HAVE_MEMSET_S 1 + /* Define to 1 if you have the fcntl function. */ #cmakedefine HAVE_FCNTL 1 @@ -255,7 +261,7 @@ /* Define to 1 if you have a working getaddrinfo function. */ #cmakedefine HAVE_GETADDRINFO 1 -/* Define to 1 if the getaddrinfo function is threadsafe. */ +/* Define to 1 if the getaddrinfo function is thread-safe. */ #cmakedefine HAVE_GETADDRINFO_THREADSAFE 1 /* Define to 1 if you have the `geteuid' function. */ @@ -709,6 +715,9 @@ ${SIZEOF_TIME_T_CODE} /* if libuv is in use */ #cmakedefine USE_LIBUV 1 +/* if HTTP/3 proxy support is available */ +#cmakedefine USE_PROXY_HTTP3 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_UV_H 1 diff --git a/lib/curl_endian.c b/lib/curl_endian.c index 864b411b68cc..b3689417a4c7 100644 --- a/lib/curl_endian.c +++ b/lib/curl_endian.c @@ -34,7 +34,7 @@ * * Parameters: * - * buf [in] - A pointer to a 2 byte buffer. + * buf [in] - A pointer to a 2-byte buffer. * * Returns the integer. */ @@ -53,7 +53,7 @@ unsigned short Curl_read16_le(const unsigned char *buf) * * Parameters: * - * buf [in] - A pointer to a 4 byte buffer. + * buf [in] - A pointer to a 4-byte buffer. * * Returns the integer. */ @@ -72,7 +72,7 @@ unsigned int Curl_read32_le(const unsigned char *buf) * * Parameters: * - * buf [in] - A pointer to a 2 byte buffer. + * buf [in] - A pointer to a 2-byte buffer. * * Returns the integer. */ diff --git a/lib/curl_fnmatch.c b/lib/curl_fnmatch.c index dde956c3ac2a..d165554f8db5 100644 --- a/lib/curl_fnmatch.c +++ b/lib/curl_fnmatch.c @@ -96,25 +96,25 @@ static int parsekeyword(const unsigned char **pattern, unsigned char *charset) #undef KEYLEN *pattern = p; /* move caller's pattern pointer */ - if(strcmp(keyword, "digit") == 0) + if(!strcmp(keyword, "digit")) charset[CURLFNM_DIGIT] = 1; - else if(strcmp(keyword, "alnum") == 0) + else if(!strcmp(keyword, "alnum")) charset[CURLFNM_ALNUM] = 1; - else if(strcmp(keyword, "alpha") == 0) + else if(!strcmp(keyword, "alpha")) charset[CURLFNM_ALPHA] = 1; - else if(strcmp(keyword, "xdigit") == 0) + else if(!strcmp(keyword, "xdigit")) charset[CURLFNM_XDIGIT] = 1; - else if(strcmp(keyword, "print") == 0) + else if(!strcmp(keyword, "print")) charset[CURLFNM_PRINT] = 1; - else if(strcmp(keyword, "graph") == 0) + else if(!strcmp(keyword, "graph")) charset[CURLFNM_GRAPH] = 1; - else if(strcmp(keyword, "space") == 0) + else if(!strcmp(keyword, "space")) charset[CURLFNM_SPACE] = 1; - else if(strcmp(keyword, "blank") == 0) + else if(!strcmp(keyword, "blank")) charset[CURLFNM_BLANK] = 1; - else if(strcmp(keyword, "upper") == 0) + else if(!strcmp(keyword, "upper")) charset[CURLFNM_UPPER] = 1; - else if(strcmp(keyword, "lower") == 0) + else if(!strcmp(keyword, "lower")) charset[CURLFNM_LOWER] = 1; else return SETCHARSET_FAIL; diff --git a/lib/curl_fopen.c b/lib/curl_fopen.c index cc888f761648..abb17eab4b87 100644 --- a/lib/curl_fopen.c +++ b/lib/curl_fopen.c @@ -42,13 +42,13 @@ */ #ifdef _WIN32 -#define PATHSEP "\\" +#define PATHSEP "\\" #define IS_SEP(x) (((x) == '/') || ((x) == '\\')) #elif defined(MSDOS) || defined(OS2) -#define PATHSEP "\\" +#define PATHSEP "\\" #define IS_SEP(x) ((x) == '\\') #else -#define PATHSEP "/" +#define PATHSEP "/" #define IS_SEP(x) ((x) == '/') #endif diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index 650d1908d0f3..07a6c1e7ed80 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -260,7 +260,7 @@ static OM_uint32 stub_gss_init_sec_context( used = curl_msnprintf(token, length, "%s:%.*s:%d:", creds, (int)target_desc.length, (const char *)target_desc.value, - ctx->sent); + (int)ctx->sent); gss_release_buffer(&minor_status, &target_desc); } @@ -302,8 +302,7 @@ static OM_uint32 stub_gss_delete_sec_context( return GSS_S_FAILURE; } - curlx_free(*context); - *context = NULL; + curlx_safefree(*context); *min = 0; return GSS_S_COMPLETE; diff --git a/lib/curl_hmac.h b/lib/curl_hmac.h index 301d44fee809..d9a697a0a595 100644 --- a/lib/curl_hmac.h +++ b/lib/curl_hmac.h @@ -24,26 +24,26 @@ * ***************************************************************************/ -#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_LIBSSH2) || defined(USE_SSL) #define HMAC_MD5_LENGTH 16 typedef CURLcode (*HMAC_hinit)(void *context); -typedef void (*HMAC_hupdate)(void *context, - const unsigned char *data, - unsigned int len); -typedef void (*HMAC_hfinal)(unsigned char *result, void *context); +typedef void (*HMAC_hupdate)(void *context, + const unsigned char *data, + unsigned int len); +typedef void (*HMAC_hfinal)(unsigned char *result, void *context); /* Per-hash function HMAC parameters. */ struct HMAC_params { - HMAC_hinit hinit; /* Initialize context procedure. */ - HMAC_hupdate hupdate; /* Update context with data. */ - HMAC_hfinal hfinal; /* Get final result procedure. */ - unsigned int ctxtsize; /* Context structure size. */ - unsigned int maxkeylen; /* Maximum key length (bytes). */ - unsigned int resultlen; /* Result length (bytes). */ + HMAC_hinit hinit; /* Initialize context procedure. */ + HMAC_hupdate hupdate; /* Update context with data. */ + HMAC_hfinal hfinal; /* Get final result procedure. */ + unsigned int ctxtsize; /* Context structure size. */ + unsigned int maxkeylen; /* Maximum key length (bytes). */ + unsigned int resultlen; /* Result length (bytes). */ }; /* HMAC computation context. */ @@ -66,7 +66,6 @@ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, const unsigned char *data, size_t datalen, unsigned char *output); - #endif #endif /* HEADER_CURL_HMAC_H */ diff --git a/lib/curl_md5.h b/lib/curl_md5.h index 8a0cc2623e38..1beaed5e66c6 100644 --- a/lib/curl_md5.h +++ b/lib/curl_md5.h @@ -38,16 +38,16 @@ typedef void (*Curl_MD5_update_func)(void *context, typedef void (*Curl_MD5_final_func)(unsigned char *result, void *context); struct MD5_params { - Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ - Curl_MD5_update_func md5_update_func; /* Update context with data */ - Curl_MD5_final_func md5_final_func; /* Get final result procedure */ - unsigned int md5_ctxtsize; /* Context structure size */ - unsigned int md5_resultlen; /* Result length (bytes) */ + Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ + Curl_MD5_update_func md5_update_func; /* Update context with data */ + Curl_MD5_final_func md5_final_func; /* Get final result procedure */ + unsigned int md5_ctxtsize; /* Context structure size */ + unsigned int md5_resultlen; /* Result length (bytes) */ }; struct MD5_context { - const struct MD5_params *md5_hash; /* Hash function definition */ - void *md5_hashctx; /* Hash function context */ + const struct MD5_params *md5_hash; /* Hash function definition */ + void *md5_hashctx; /* Hash function context */ }; extern const struct MD5_params Curl_DIGEST_MD5; diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index 4b2007bbad89..58a446c95db0 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -49,6 +49,13 @@ in NTLM type-3 messages. */ +#ifdef USE_GNUTLS +#include +#if NETTLE_VERSION_MAJOR < 4 +#define USE_GNUTLS_DES +#endif +#endif + #if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT) # include @@ -63,7 +70,7 @@ # include # define USE_WOLFSSL_DES -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) # include # define USE_CURL_DES_SET_ODD_PARITY #elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) @@ -175,7 +182,7 @@ static void setup_des_key(const unsigned char *key_56, Des *des) wc_Des_SetKey(des, key, NULL, 0); } -#elif defined(USE_GNUTLS) +#elif defined(USE_GNUTLS_DES) static void setup_des_key(const unsigned char *key_56, struct des_ctx *des) { char key[8]; @@ -244,6 +251,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, char key[8]; } blob; DWORD len = 8; + BOOL res; /* Acquire the crypto provider */ if(!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL, @@ -273,19 +281,19 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, memcpy(out, in, 8); /* Perform the encryption */ - CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len); + res = CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len); CryptDestroyKey(hkey); CryptReleaseContext(hprov, 0); - return TRUE; + return res; } #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 + * 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, @@ -314,7 +322,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, 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) +#elif defined(USE_GNUTLS_DES) struct des_ctx des; setup_des_key(keys, &des); des_encrypt(&des, 8, results, plaintext); @@ -367,7 +375,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, 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) +#elif defined(USE_GNUTLS_DES) struct des_ctx des; setup_des_key(pw, &des); des_encrypt(&des, 8, lmbuffer, magic); @@ -625,7 +633,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash, * * ntlmv2hash [in] - The NTLMv2 hash (16 bytes) * challenge_client [in] - The client nonce (8 bytes) - * challenge_client [in] - The server challenge (8 bytes) + * challenge_server [in] - The server challenge (8 bytes) * lmresp [out] - The LMv2 response (24 bytes) * * Returns CURLE_OK on success. diff --git a/lib/curl_ntlm_core.h b/lib/curl_ntlm_core.h index f96bf0ada5ba..df24158f8e30 100644 --- a/lib/curl_ntlm_core.h +++ b/lib/curl_ntlm_core.h @@ -30,7 +30,7 @@ struct ntlmdata; /* Helpers to generate function byte arguments in little endian order */ -#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) +#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) #define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \ ((int)(((x) >> 16) & 0xff)), ((int)(((x) >> 24) & 0xff)) diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 60f085901f95..7e867d753fe7 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -35,7 +35,7 @@ #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 "urldata.h" @@ -276,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->conn->user[0]) + if(data->conn->creds) return TRUE; /* EXTERNAL can authenticate without a username and/or password */ @@ -299,13 +299,15 @@ struct sasl_ctx { static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx) { - if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) { + if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && + !Curl_creds_has_passwd(sctx->conn->creds)) { sctx->mech = SASL_MECH_STRING_EXTERNAL; sctx->state1 = SASL_EXTERNAL; sctx->sasl->authused = SASL_MECH_EXTERNAL; if(sctx->sasl->force_ir || data->set.sasl_ir) - Curl_auth_create_external_message(sctx->conn->user, &sctx->resp); + Curl_auth_create_external_message( + Curl_creds_user(sctx->conn->creds), &sctx->resp); return TRUE; } return FALSE; @@ -316,10 +318,9 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) { 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] ? - data->set.str[STRING_SERVICE_NAME] : - sctx->sasl->params->service; + Curl_auth_user_contains_domain(sctx->conn->creds)) { + const char *service = Curl_creds_has_sasl_service(sctx->conn->creds) ? + Curl_creds_sasl_service(sctx->conn->creds) : sctx->sasl->params->service; sctx->sasl->mutual_auth = FALSE; sctx->mech = SASL_MECH_STRING_GSSAPI; @@ -330,9 +331,9 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) { struct kerberos5data *krb5 = Curl_auth_krb5_get(sctx->conn); sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_gssapi_user_message(data, sctx->conn->user, - sctx->conn->passwd, - service, sctx->conn->host.name, + Curl_auth_create_gssapi_user_message(data, sctx->conn->creds, + service, + sctx->conn->origin->hostname, (bool)sctx->sasl->mutual_auth, NULL, krb5, &sctx->resp); } @@ -374,8 +375,7 @@ static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) Curl_bufref_init(&nullmsg); sctx->state1 = SASL_GSASL; sctx->state2 = SASL_GSASL; - sctx->result = Curl_auth_gsasl_start(data, sctx->conn->user, - sctx->conn->passwd, gsasl); + sctx->result = Curl_auth_gsasl_start(data, sctx->conn->creds, gsasl); if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir)) sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp); return TRUE; @@ -411,9 +411,8 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) { 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 *service = Curl_creds_has_sasl_service(sctx->conn->creds) ? + Curl_creds_sasl_service(sctx->conn->creds) : sctx->sasl->params->service; const char *hostname; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, NULL); @@ -426,9 +425,7 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) { struct ntlmdata *ntlm = Curl_auth_ntlm_get(sctx->conn, FALSE); sctx->result = !ntlm ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_ntlm_type1_message(data, - sctx->conn->user, - sctx->conn->passwd, + Curl_auth_create_ntlm_type1_message(data, sctx->conn->creds, service, hostname, ntlm, &sctx->resp); } @@ -440,11 +437,8 @@ 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->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? - data->set.str[STRING_BEARER] : NULL; - - if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { + if(Curl_creds_has_oauth_bearer(data->state.creds) && + (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { const char *hostname; int port; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -456,9 +450,8 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) sctx->result = - Curl_auth_create_oauth_bearer_message(sctx->conn->user, - hostname, port, - oauth_bearer, &sctx->resp); + Curl_auth_create_oauth_bearer_message(sctx->conn->creds, + hostname, port, &sctx->resp); return TRUE; } return FALSE; @@ -466,19 +459,15 @@ 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->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ? - data->set.str[STRING_BEARER] : NULL; - - if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { + if(Curl_creds_has_oauth_bearer(sctx->conn->creds) && + (sctx->enabledmechs & SASL_MECH_XOAUTH2)) { sctx->mech = SASL_MECH_STRING_XOAUTH2; sctx->state1 = SASL_OAUTH2; sctx->sasl->authused = SASL_MECH_XOAUTH2; if(sctx->sasl->force_ir || data->set.sasl_ir) - sctx->result = Curl_auth_create_xoauth_bearer_message(sctx->conn->user, - oauth_bearer, - &sctx->resp); + sctx->result = Curl_auth_create_xoauth_bearer_message( + sctx->conn->creds, &sctx->resp); return TRUE; } return FALSE; @@ -493,9 +482,7 @@ static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx) if(sctx->sasl->force_ir || data->set.sasl_ir) sctx->result = - Curl_auth_create_plain_message(sctx->conn->sasl_authzid, - sctx->conn->user, sctx->conn->passwd, - &sctx->resp); + Curl_auth_create_plain_message(sctx->conn->creds, &sctx->resp); return TRUE; } return FALSE; @@ -510,7 +497,8 @@ static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx) sctx->sasl->authused = SASL_MECH_LOGIN; if(sctx->sasl->force_ir || data->set.sasl_ir) - Curl_auth_create_login_message(sctx->conn->user, &sctx->resp); + Curl_auth_create_login_message( + Curl_creds_user(sctx->conn->creds), &sctx->resp); return TRUE; } return FALSE; @@ -599,13 +587,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct bufref resp; const char *hostname; int port; -#if defined(USE_KERBEROS5) || defined(USE_NTLM) || \ - !defined(CURL_DISABLE_DIGEST_AUTH) - const char *service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : - sasl->params->service; -#endif - const char *oauth_bearer = data->set.str[STRING_BEARER]; struct bufref serverdata; Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); @@ -633,18 +614,17 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, *progress = SASL_DONE; return result; case SASL_PLAIN: - result = Curl_auth_create_plain_message(conn->sasl_authzid, - conn->user, conn->passwd, &resp); + result = Curl_auth_create_plain_message(conn->creds, &resp); break; case SASL_LOGIN: - Curl_auth_create_login_message(conn->user, &resp); + Curl_auth_create_login_message(Curl_creds_user(conn->creds), &resp); newstate = SASL_LOGIN_PASSWD; break; case SASL_LOGIN_PASSWD: - Curl_auth_create_login_message(conn->passwd, &resp); + Curl_auth_create_login_message(Curl_creds_passwd(conn->creds), &resp); break; case SASL_EXTERNAL: - Curl_auth_create_external_message(conn->user, &resp); + Curl_auth_create_external_message(Curl_creds_user(conn->creds), &resp); break; #ifdef USE_GSASL case SASL_GSASL: @@ -662,15 +642,16 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_CRAMMD5: result = get_server_message(sasl, data, &serverdata); if(!result) - result = Curl_auth_create_cram_md5_message(&serverdata, conn->user, - conn->passwd, &resp); + result = Curl_auth_create_cram_md5_message(&serverdata, conn->creds, + &resp); break; case SASL_DIGESTMD5: result = get_server_message(sasl, data, &serverdata); if(!result) result = Curl_auth_create_digest_md5_message(data, &serverdata, - conn->user, conn->passwd, - service, &resp); + conn->creds, + sasl->params->service, + &resp); if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) newstate = SASL_DIGESTMD5_RESP; break; @@ -684,9 +665,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, /* Create the type-1 message */ struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); result = !ntlm ? CURLE_OUT_OF_MEMORY : - Curl_auth_create_ntlm_type1_message(data, - conn->user, conn->passwd, - service, hostname, + Curl_auth_create_ntlm_type1_message(data, conn->creds, + sasl->params->service, hostname, ntlm, &resp); newstate = SASL_NTLM_TYPE2MSG; break; @@ -699,9 +679,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(!result) result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm); if(!result) - result = Curl_auth_create_ntlm_type3_message(data, conn->user, - conn->passwd, ntlm, - &resp); + result = Curl_auth_create_ntlm_type3_message(data, conn->creds, + ntlm, &resp); break; } #endif @@ -710,8 +689,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_GSSAPI: { 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, + Curl_auth_create_gssapi_user_message(data, conn->creds, + sasl->params->service, + conn->origin->hostname, (bool)sasl->mutual_auth, NULL, krb5, &resp); newstate = SASL_GSSAPI_TOKEN; @@ -726,7 +706,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, else if(sasl->mutual_auth) { /* Decode the user token challenge and create the optional response message */ - result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, + result = Curl_auth_create_gssapi_user_message(data, NULL, NULL, NULL, (bool)sasl->mutual_auth, &serverdata, @@ -735,10 +715,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, } else /* Decode the security challenge and create the response message */ - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - krb5, &resp); + result = Curl_auth_create_gssapi_security_message( + data, Curl_creds_sasl_authzid(conn->creds), &serverdata, + krb5, &resp); } break; case SASL_GSSAPI_NO_DATA: @@ -749,10 +728,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, if(!krb5) result = CURLE_OUT_OF_MEMORY; else - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - krb5, &resp); + result = Curl_auth_create_gssapi_security_message( + data, Curl_creds_sasl_authzid(conn->creds), &serverdata, + krb5, &resp); } break; #endif @@ -760,18 +738,16 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, case SASL_OAUTH2: /* Create the authorization message */ if(sasl->authused == SASL_MECH_OAUTHBEARER) { - result = Curl_auth_create_oauth_bearer_message(conn->user, + result = Curl_auth_create_oauth_bearer_message(conn->creds, hostname, port, - oauth_bearer, &resp); /* Failures maybe sent by the server as continuations for OAUTHBEARER */ newstate = SASL_OAUTH2_RESP; } else - result = Curl_auth_create_xoauth_bearer_message(conn->user, - oauth_bearer, + result = Curl_auth_create_xoauth_bearer_message(conn->creds, &resp); break; @@ -861,7 +837,7 @@ 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->conn->user[0]) + if(!Curl_creds_has_user(data->conn->creds)) infof(data, "SASL: %s is missing username", mname); } } @@ -903,7 +879,8 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) "auth mechanisms"); else { infof(data, "SASL: no auth mechanism offered could be selected"); - if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0]) + if((enabledmechs & SASL_MECH_EXTERNAL) && + Curl_creds_has_passwd(data->conn->creds)) infof(data, "SASL: auth EXTERNAL not chosen with password"); sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs, CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL); @@ -918,10 +895,10 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data) 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, - data->set.str[STRING_BEARER] ? + Curl_creds_has_oauth_bearer(data->conn->creds) ? NULL : "CURLOPT_XOAUTH2_BEARER"); sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE, - data->set.str[STRING_BEARER] ? + Curl_creds_has_oauth_bearer(data->conn->creds) ? NULL : "CURLOPT_XOAUTH2_BEARER"); } #endif /* CURLVERBOSE */ diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 6578b664d422..2b4a5141dd6d 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -256,7 +256,7 @@ /* please, do it beyond the point further indicated in this file. */ /* ================================================================ */ -/* Give calloc a chance to be dragging in early, so we do not redefine */ +/* Give calloc a chance to be included early, so we do not redefine */ #ifdef HAVE_THREADS_POSIX # include #endif @@ -514,7 +514,7 @@ # undef HAVE_FCNTL # undef HAVE_FCNTL_O_NONBLOCK # else - /* use libc networking and hence close() and fnctl() */ + /* use libc networking and hence close() and fcntl() */ # undef HAVE_CLOSESOCKET_CAMEL # undef HAVE_IOCTLSOCKET_CAMEL # endif @@ -640,7 +640,7 @@ # endif #endif -#if (SIZEOF_CURL_OFF_T < 8) +#if SIZEOF_CURL_OFF_T < 8 #error "too small curl_off_t" #else /* assume SIZEOF_CURL_OFF_T == 8 */ @@ -651,7 +651,7 @@ #define FMT_OFF_T CURL_FORMAT_CURL_OFF_T #define FMT_OFF_TU CURL_FORMAT_CURL_OFF_TU -#if (SIZEOF_TIME_T == 4) +#if SIZEOF_TIME_T == 4 # ifdef HAVE_TIME_T_UNSIGNED # define TIME_T_MAX UINT_MAX # define TIME_T_MIN 0 @@ -1191,6 +1191,15 @@ typedef unsigned int curl_bit; #define SOCKEWOULDBLOCK EWOULDBLOCK #endif +/* The socket error may be EWOULDBLOCK or on some systems EAGAIN when + it returned due to its inability to send/read data without blocking. + We treat both error codes the same here. */ +#if !defined(USE_WINSOCK) && EAGAIN != SOCKEWOULDBLOCK +#define SOCK_EAGAIN(e) ((e) == SOCKEWOULDBLOCK || (e) == EAGAIN) +#else +#define SOCK_EAGAIN(e) ((e) == SOCKEWOULDBLOCK) +#endif + /* * Macro argv_item_t hides platform details to code using it. */ @@ -1383,6 +1392,20 @@ extern curl_calloc_callback Curl_ccalloc; (ptr) = NULL; \ } while(0) +/* Same as curlx_safefree() but zeroes memory before freeing */ +#define curlx_safefreezero(ptr, size) \ + do { \ + curlx_freezero(ptr, size); \ + (ptr) = NULL; \ + } while(0) + +/* Same as curlx_safefreezero() but determines length with strlen() */ +#define curlx_safefreezeroz(ptr) \ + do { \ + curlx_freezeroz(ptr); \ + (ptr) = NULL; \ + } while(0) + #include /* for CURL_EXTERN, curl_socket_t, mprintf.h */ #ifdef DEBUGBUILD @@ -1596,7 +1619,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, #endif #if defined(USE_UNIX_SOCKETS) && defined(_WIN32) -/* Offered by mingw-w64 v10+. MS SDK 10.17763/~VS2017+. */ +/* Offered by mingw-w64 v10+, MS SDK 10.0.16299.0/VS2017 15.4+ */ #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR >= 10) # include #elif !defined(UNIX_PATH_MAX) /* Replicate logic present in afunix.h */ @@ -1632,7 +1655,7 @@ typedef struct sockaddr_un { /* The code is compiled with C++ compiler. C++ always supports 'inline'. */ # define CURL_INLINE inline /* 'inline' keyword supported */ -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901 +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 (and later) supports 'inline' keyword */ # define CURL_INLINE inline /* 'inline' keyword supported */ #elif defined(__GNUC__) && __GNUC__ >= 3 @@ -1662,4 +1685,43 @@ typedef struct sockaddr_un { #define NOVERBOSE(x) x #endif +/* For FreeBSD it is included from curl/curl.h */ +#if defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include /* for __DragonFly_version, OpenBSD, + __NetBSD_Version__ */ +#endif + +#ifndef _CURL_LOCAL_MEMZERO /* to be removed after a couple of releases */ +#ifdef _WIN32 +#if defined(_MSC_VER) && defined(NTDDI_VERSION) && \ + (NTDDI_VERSION >= 0x0A000010) /* MS SDK 10.0.26100.0+ */ +#pragma comment(lib, "volatileaccessu.lib") +#define curlx_memzero(buf, size) SecureZeroMemory2(buf, size) +#else +#define curlx_memzero(buf, size) SecureZeroMemory(buf, size) +#endif +#elif defined(HAVE_MEMSET_S) +#define curlx_memzero(buf, size) (void)memset_s(buf, size, 0, size) +#elif defined(HAVE_MEMSET_EXPLICIT) +#define curlx_memzero(buf, size) (void)memset_explicit(buf, 0, size) +#elif defined(__CYGWIN__) || \ + (defined(__NEWLIB__) && !defined(__CLIB2__)) || \ + (defined(__GLIBC__) && \ + (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) || \ + (defined(__DragonFly__) && __DragonFly_version >= 500600 /* v5.6+ */) || \ + (defined(__FreeBSD__) && __FreeBSD_version >= 1100037 /* v11.0+ */) || \ + (defined(__OpenBSD__) && OpenBSD >= 201405 /* v5.5+ */) +#define curlx_memzero(buf, size) explicit_bzero(buf, size) +#elif defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 /* v7.2+ */ +#define curlx_memzero(buf, size) (void)explicit_memset(buf, 0, size) +#endif +#endif /* !_CURL_LOCAL_MEMZERO */ + +#ifndef curlx_memzero +#define USE_CURLX_MEMZERO +void curlx_memzero(void *buf, size_t size); +#endif +void curlx_freezero(void *buf, size_t size); +void curlx_freezeroz(void *buf); + #endif /* HEADER_CURL_SETUP_H */ diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 73b959c91df0..c429bba8f268 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -44,7 +44,7 @@ # define USE_OPENSSL_SHA512_256 1 # define HAS_SHA512_256_IMPLEMENTATION 1 # ifdef __NetBSD__ -/* Some NetBSD versions has a bug in SHA-512/256. +/* Some NetBSD versions have a bug in SHA-512/256. * See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 * The problematic versions: * - NetBSD before 9.4 @@ -54,7 +54,6 @@ * NetBSD 10.99.11 development. * It is safe to apply the workaround even if the bug is not present, as * the workaround reduces performance slightly. */ -# include # if __NetBSD_Version__ < 904000000 || \ (__NetBSD_Version__ >= 999000000 && \ __NetBSD_Version__ < 1000000000) || \ @@ -75,7 +74,8 @@ #endif #if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS) -# include +# include +# include # ifdef SHA512_256_DIGEST_SIZE # define USE_GNUTLS_SHA512_256 1 # endif @@ -102,7 +102,7 @@ typedef EVP_MD_CTX *Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return CURLE_OK if succeed, @@ -145,7 +145,7 @@ static CURLcode Curl_sha512_256_update(void *context, Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; if(!EVP_DigestUpdate(*ctx, data, length)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } @@ -168,14 +168,14 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) /* Use a larger buffer to work around a bug in NetBSD: https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */ unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2]; - result = EVP_DigestFinal_ex(*ctx, - tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; + result = EVP_DigestFinal_ex(*ctx, tmp_digest, NULL) ? + CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; if(result == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); - explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); + curlx_memzero(tmp_digest, sizeof(tmp_digest)); #else /* !NEED_NETBSD_SHA512_256_WORKAROUND */ result = EVP_DigestFinal_ex(*ctx, digest, NULL) ? - CURLE_OK : CURLE_SSL_CIPHER; + CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT; #endif /* NEED_NETBSD_SHA512_256_WORKAROUND */ EVP_MD_CTX_destroy(*ctx); @@ -206,7 +206,7 @@ static CURLcode Curl_sha512_256_update(void *ctx, do { word32 ilen = (word32)CURLMIN(length, UINT_MAX); if(wc_Sha512_256Update(ctx, data, ilen)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; length -= ilen; data += ilen; } while(length); @@ -216,7 +216,7 @@ static CURLcode Curl_sha512_256_update(void *ctx, static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) { if(wc_Sha512_256Final(ctx, digest)) - return CURLE_SSL_CIPHER; + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } @@ -231,7 +231,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) typedef struct sha512_256_ctx Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return always CURLE_OK @@ -262,7 +262,7 @@ static CURLcode Curl_sha512_256_update(void *context, { Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; - DEBUGASSERT((data != NULL) || (length == 0)); + DEBUGASSERT(data || (length == 0)); sha512_256_update(ctx, length, (const uint8_t *)data); @@ -281,8 +281,12 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; +#if NETTLE_VERSION_MAJOR >= 4 + sha512_256_digest(ctx, (uint8_t *)digest); +#else sha512_256_digest(ctx, (size_t)CURL_SHA512_256_DIGEST_SIZE, (uint8_t *)digest); +#endif return CURLE_OK; } @@ -296,7 +300,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) #ifdef __GNUC__ # if defined(__has_attribute) && defined(__STDC_VERSION__) -# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901 +# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901L # define CURL_FORCEINLINE CURL_INLINE __attribute__((always_inline)) # endif # endif @@ -430,7 +434,7 @@ struct Curl_sha512_256ctx { typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx; /** - * Initialise structure for SHA-512/256 calculation. + * Initialize structure for SHA-512/256 calculation. * * @param context the calculation context * @return always CURLE_OK @@ -456,7 +460,7 @@ static CURLcode Curl_sha512_256_init(void *context) ctx->H[6] = UINT64_C(0x2B0199FC2C85B8AA); ctx->H[7] = UINT64_C(0x0EB72DDC81C52CA2); - /* Initialise number of bytes and high part of number of bits. */ + /* Initialize number of bytes and high part of number of bits. */ ctx->count = UINT64_C(0); ctx->count_bits_hi = UINT64_C(0); @@ -494,14 +498,10 @@ static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS], /* Four 'Sigma' macro functions. See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */ -#define SIG0(x) \ - (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39)) -#define SIG1(x) \ - (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41)) -#define sig0(x) \ - (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7)) -#define sig1(x) \ - (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6)) +#define SIG0(x) (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39)) +#define SIG1(x) (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41)) +#define sig0(x) (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7)) +#define sig1(x) (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6)) if(1) { unsigned int t; @@ -645,7 +645,7 @@ static CURLcode Curl_sha512_256_update(void *context, /* the void pointer here is required to mute Intel compiler warning */ void * const ctx_buf = ctx->buffer; - DEBUGASSERT((data != NULL) || (length == 0)); + DEBUGASSERT(data || (length == 0)); if(length == 0) return CURLE_OK; /* Shortcut, do nothing */ @@ -711,7 +711,7 @@ static CURLcode Curl_sha512_256_update(void *context, static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { struct Curl_sha512_256ctx * const ctx = (struct Curl_sha512_256ctx *)context; - uint64_t num_bits; /* Number of processed bits */ + 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; diff --git a/lib/curl_share.c b/lib/curl_share.c index 386a2b547d33..94469bc3ef6e 100644 --- a/lib/curl_share.c +++ b/lib/curl_share.c @@ -260,7 +260,7 @@ CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) case CURL_LOCK_DATA_CONNECT: /* It is safe to set this option several times on a share. */ - if(!share->cpool.initialised) { + if(!share->cpool.initialized) { Curl_cpool_init(&share->cpool, share->admin, share, 103); } break; diff --git a/lib/curl_share.h b/lib/curl_share.h index 69001be705a5..1d49a8d1dd3c 100644 --- a/lib/curl_share.h +++ b/lib/curl_share.h @@ -81,9 +81,9 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type, 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 << CURL_LOCK_DATA_SSL_SESSION))) +#define CURL_SHARE_ssl_scache(data) \ + ((data)->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, diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 3779d5175340..2bd7eb4be88e 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -52,25 +52,25 @@ extern PSecurityFunctionTable Curl_pSecFn; #define SP_NAME_NEGOTIATE "Negotiate" #define SP_NAME_KERBEROS "Kerberos" -/* Offered by mingw-w64 v9+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ #ifndef ISC_REQ_USE_HTTP_STYLE #define ISC_REQ_USE_HTTP_STYLE 0x01000000 #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_INVALID_PARAMETER #define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_DELEGATION_POLICY #define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_E_POLICY_NLTM_ONLY #define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL) #endif -/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */ #ifndef SEC_I_SIGNATURE_NEEDED #define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL) #endif diff --git a/lib/curl_trc.c b/lib/curl_trc.c index c6115cf7f636..f8287e420fd5 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -29,8 +29,9 @@ #include "multiif.h" #include "cf-dns.h" +#include "cf-recvbuf.h" #include "cf-socket.h" -#include "connect.h" +#include "cf-setup.h" #include "http2.h" #include "http_proxy.h" #include "cf-h1-proxy.h" @@ -563,6 +564,9 @@ static struct trc_cft_def trc_cfts[] = { { &Curl_cft_unix, TRC_CT_NETWORK }, { &Curl_cft_tcp_accept, TRC_CT_NETWORK }, { &Curl_cft_ip_happy, TRC_CT_NETWORK }, +#ifndef CURL_DISABLE_WEBSOCKETS + { &Curl_cft_recvbuf, TRC_CT_PROTOCOL }, +#endif { &Curl_cft_setup, TRC_CT_PROTOCOL }, #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2) { &Curl_cft_nghttp2, TRC_CT_PROTOCOL }, @@ -578,6 +582,9 @@ static struct trc_cft_def trc_cfts[] = { { &Curl_cft_h1_proxy, TRC_CT_PROXY }, #ifdef USE_NGHTTP2 { &Curl_cft_h2_proxy, TRC_CT_PROXY }, +#endif +#if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) + { &Curl_cft_h3_proxy, TRC_CT_PROXY }, #endif { &Curl_cft_http_proxy, TRC_CT_PROXY }, #endif /* !CURL_DISABLE_HTTP */ diff --git a/lib/curlx/fopen.c b/lib/curlx/fopen.c index 6733010468ad..37ca02671afe 100644 --- a/lib/curlx/fopen.c +++ b/lib/curlx/fopen.c @@ -80,7 +80,7 @@ static wchar_t *fn_convert_UTF8_to_wchar(const char *str_utf8) } #endif -/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */ +/* 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 *); @@ -247,7 +247,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out) CURLX_FREE(ibuf); CURLX_FREE(obuf); #endif - return *out ? true : false; + return !!*out; } #ifndef CURL_WINDOWS_UWP diff --git a/lib/curlx/strcopy.c b/lib/curlx/strcopy.c index 3e58ea24f1bd..3b80930a1950 100644 --- a/lib/curlx/strcopy.c +++ b/lib/curlx/strcopy.c @@ -30,10 +30,10 @@ * * 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. + * 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. + * 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 */ diff --git a/lib/curlx/strdup.c b/lib/curlx/strdup.c index 3c967dbe0a5d..8e788ea34c01 100644 --- a/lib/curlx/strdup.c +++ b/lib/curlx/strdup.c @@ -94,3 +94,32 @@ void *curlx_memdup0(const char *src, size_t length) buf[length] = 0; return buf; } + +#ifdef USE_CURLX_MEMZERO +static void *(* const volatile p_curlx_memset)(void *buf, int val, + size_t size) = memset; + +/* Local fallback in case there is no system function to securely zero a memory + buffer. */ +void curlx_memzero(void *buf, size_t size) +{ + if(buf) + p_curlx_memset(buf, 0, size); +} +#endif + +/* Free 'buf' after zeroing its content. */ +void curlx_freezero(void *buf, size_t size) +{ + if(buf) + curlx_memzero(buf, size); + curlx_free(buf); +} + +/* Free 'buf' after zeroing its content, where 'buf' is null-terminated. */ +void curlx_freezeroz(void *buf) +{ + if(buf) + curlx_memzero(buf, strlen(buf)); + curlx_free(buf); +} diff --git a/lib/curlx/strerr.c b/lib/curlx/strerr.c index b53173c57839..8e906cc6bdfb 100644 --- a/lib/curlx/strerr.c +++ b/lib/curlx/strerr.c @@ -190,7 +190,7 @@ static const char *get_winsock_error(int err, char *buf, size_t len) p = "Winsock library is not ready"; break; case WSANOTINITIALISED: - p = "Winsock library not initialised"; + p = "Winsock library not initialized"; break; case WSAVERNOTSUPPORTED: p = "Winsock version not supported"; @@ -268,7 +268,7 @@ const char *curlx_strerror(int err, char *buf, size_t buflen) !get_winsock_error(err, buf, buflen) && #endif !curlx_get_winapi_error((DWORD)err, buf, buflen)) - SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, err); + SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, (unsigned int)err); #else /* !_WIN32 */ #if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R) diff --git a/lib/curlx/strparse.c b/lib/curlx/strparse.c index 7866b2087ab7..736f62050438 100644 --- a/lib/curlx/strparse.c +++ b/lib/curlx/strparse.c @@ -45,13 +45,17 @@ void curlx_str_trim(struct Curl_str *out, size_t len) } /* Get a word until the first DELIM or end of string. At least one byte long. - return non-zero on error */ + return non-zero on error. If 'max' is zero, it will always return error. */ int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max, char delim) { - const char *s = *linep; + const char *s; size_t len = 0; - DEBUGASSERT(linep && *linep && out && max && delim); + DEBUGASSERT(linep); + DEBUGASSERT(*linep); + DEBUGASSERT(out); + DEBUGASSERT(delim); + s = *linep; curlx_str_init(out); while(*s && (*s != delim)) { diff --git a/lib/cw-out.c b/lib/cw-out.c index 2af074e5871c..fad89b392cc8 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -195,7 +195,7 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx, 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:// now, and it cannot pause since the transfer is + 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; diff --git a/lib/cw-pause.c b/lib/cw-pause.c index ec611879d1e8..5561a8d7b7f6 100644 --- a/lib/cw-pause.c +++ b/lib/cw-pause.c @@ -117,7 +117,8 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type, (const char *)buf, wlen); CURL_TRC_WRITE(data, "[PAUSE] flushed %zu/%zu bytes, type=%x -> %d", - wlen, ctx->buf_total, (*plast)->type, result); + wlen, ctx->buf_total, (unsigned int)(*plast)->type, + (int)result); Curl_bufq_skip(&(*plast)->b, wlen); DEBUGASSERT(ctx->buf_total >= wlen); ctx->buf_total -= wlen; @@ -128,7 +129,8 @@ static CURLcode cw_pause_flush(struct Curl_easy *data, result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type, (const char *)buf, 0); CURL_TRC_WRITE(data, "[PAUSE] flushed 0/%zu bytes, type=%x -> %d", - ctx->buf_total, (*plast)->type, result); + ctx->buf_total, (unsigned int)(*plast)->type, + (int)result); } if(Curl_bufq_is_empty(&(*plast)->b)) { @@ -165,7 +167,7 @@ static CURLcode cw_pause_write(struct Curl_easy *data, wtype &= ~CLIENTWRITE_EOS; result = Curl_cwriter_write(data, writer->next, wtype, buf, wlen); CURL_TRC_WRITE(data, "[PAUSE] writing %zu/%zu bytes of type %x -> %d", - wlen, blen, wtype, result); + wlen, blen, (unsigned int)wtype, (int)result); if(result) return result; buf += wlen; @@ -191,8 +193,8 @@ static CURLcode cw_pause_write(struct Curl_easy *data, result = Curl_bufq_cwrite(&ctx->buf->b, buf, blen, &nwritten); } CURL_TRC_WRITE(data, "[PAUSE] buffer %zu more bytes of type %x, " - "total=%zu -> %d", nwritten, type, ctx->buf_total + wlen, - result); + "total=%zu -> %d", nwritten, (unsigned int)type, + ctx->buf_total + wlen, (int)result); if(result) return result; buf += nwritten; diff --git a/lib/dict.c b/lib/dict.c index 7b83c6cff193..db25d5a7216c 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -148,7 +148,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) *done = TRUE; /* unconditionally */ - /* url-decode path before further evaluation */ + /* URL-decode path before further evaluation */ result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL); if(result) return result; diff --git a/lib/dllmain.c b/lib/dllmain.c index f715b6d30161..5aa6565010d8 100644 --- a/lib/dllmain.c +++ b/lib/dllmain.c @@ -31,7 +31,7 @@ #if defined(_WIN32) && !defined(CURL_STATICLIB) #if defined(USE_OPENSSL) && \ - !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) && \ + !defined(OPENSSL_IS_AWSLC) && !defined(OPENSSL_IS_BORINGSSL) && \ !defined(LIBRESSL_VERSION_NUMBER) #define PREVENT_OPENSSL_MEMLEAK #endif diff --git a/lib/dnscache.c b/lib/dnscache.c index 20f6b2171441..9c8341e56fb4 100644 --- a/lib/dnscache.c +++ b/lib/dnscache.c @@ -351,7 +351,7 @@ UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data, if(num_addrs > 1) { struct Curl_addrinfo **nodes; - CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs); + CURL_TRC_DNS(data, "Shuffling %d addresses", num_addrs); nodes = curlx_malloc(num_addrs * sizeof(*nodes)); if(nodes) { @@ -616,20 +616,6 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, 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 @@ -789,7 +775,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) result = Curl_str2addr(address, port, &ai); if(result) { - infof(data, "Resolve address '%s' found illegal", address); + infof(data, "Resolve IP address '%s' found is illegal", address); goto err; } @@ -842,7 +828,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data) Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); } - /* put this new host in the cache, an overridy for ALL dns queries */ + /* put this new host in the cache, an override for ALL dns queries */ dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, &head, curlx_str(&source), curlx_strlen(&source), port, permanent); diff --git a/lib/dnscache.h b/lib/dnscache.h index ebe25f6dd416..9239977cf8c9 100644 --- a/lib/dnscache.h +++ b/lib/dnscache.h @@ -80,11 +80,6 @@ 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, diff --git a/lib/doh.c b/lib/doh.c index 30441358ca54..8b643aa0b428 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -259,7 +259,6 @@ static void doh_probe_done(struct Curl_easy *data, 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); @@ -319,7 +318,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, sizeof(doh_req->req_body), &doh_req->req_body_len); if(d) { - failf(data, "Failed to encode DoH packet [%d]", d); + failf(data, "Failed to encode DoH packet [%d]", (int)d); result = CURLE_OUT_OF_MEMORY; goto error; } @@ -418,7 +417,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data, } (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, - (long)data->set.ssl.primary.ssl_options); + ((long)data->set.ssl.primary.ssl_options & + ~CURLSSLOPT_AUTO_CLIENT_CERT)); doh->state.internal = TRUE; doh->master_mid = data->mid; /* master transfer of this one */ @@ -724,7 +724,9 @@ UNITTEST void de_init(struct dohentry *de) curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME); } -/* @unittest 1655 */ +/* TTL value cap */ +#define MAX_DNS_TTL 86400U /* 24 hours */ +/* @unittest 1650 */ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, size_t dohlen, DNStype dnstype, @@ -794,6 +796,8 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, return DOH_DNS_OUT_OF_RANGE; ttl = doh_get32bit(doh, index); + if(ttl > MAX_DNS_TTL) + ttl = MAX_DNS_TTL; if(ttl < d->ttl) d->ttl = ttl; index += 4; @@ -1087,10 +1091,14 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf, DEBUGASSERT(buf && remaining && dnsname); if(!buf || !remaining || !dnsname || !*remaining) return CURLE_OUT_OF_MEMORY; - curlx_dyn_init(&thename, CURL_MAXLEN_host_name); + curlx_dyn_init(&thename, CURL_MAXLEN_HOST_NAME); rem = *remaining; cp = *buf; clen = *cp++; + /* RFC 9460 says it must be uncompressed */ + if(clen > 63) + return CURLE_WEIRD_SERVER_REPLY; + if(clen == 0) { /* special case - return "." as name */ if(curlx_dyn_addn(&thename, ".", 1)) @@ -1112,6 +1120,11 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf, return CURLE_OUT_OF_MEMORY; } clen = *cp++; + if(clen > 63) { + /* invalid format */ + curlx_dyn_free(&thename); + return CURLE_WEIRD_SERVER_REPLY; + } } *buf = cp; *remaining = rem - 1; diff --git a/lib/doh.h b/lib/doh.h index 428b230a5817..cd2aad92544c 100644 --- a/lib/doh.h +++ b/lib/doh.h @@ -167,7 +167,6 @@ void Curl_doh_cleanup(struct Curl_easy *data, struct Curl_resolv_async *async); #define Curl_doh_wanted(d) (!!(d)->set.doh) - #else /* CURL_DISABLE_DOH */ #define Curl_doh(a, b) NULL #define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST diff --git a/lib/easy.c b/lib/easy.c index a472d6ddc52e..d60bdaed7ba3 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -78,8 +78,8 @@ #include "easy_lock.h" /* true globals -- for curl_global_init() and curl_global_cleanup() */ -static unsigned int initialized; -static long easy_init_flags; +static unsigned int initialized; +static long easy_init_flags; #ifdef GLOBAL_INIT_IS_THREADSAFE @@ -942,7 +942,7 @@ static void dupeasy_meta_freeentry(void *p) /* 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); + DEBUGASSERT(!p); } /* @@ -1052,6 +1052,11 @@ CURL *curl_easy_duphandle(CURL *curl) (void)Curl_hsts_loadfile(outcurl, outcurl->hsts, outcurl->set.str[STRING_HSTS]); (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); + + /* Copy entries learned at runtime. (E.g. Strict-Transport-Security + headers.) */ + if(Curl_hsts_copy(outcurl->hsts, data->hsts)) + goto fail; } #endif diff --git a/lib/fake_addrinfo.c b/lib/fake_addrinfo.c index 5a89202064de..6b04b5d2f560 100644 --- a/lib/fake_addrinfo.c +++ b/lib/fake_addrinfo.c @@ -64,7 +64,7 @@ static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead) const char *name = aihead->name; /* traverse the addrinfo list */ - for(ai = aihead->nodes; ai != NULL; ai = ai->ai_next) { + for(ai = aihead->nodes; ai; ai = ai->ai_next) { size_t ss_size; size_t namelen = name ? strlen(name) + 1 : 0; /* ignore elements with unsupported address family, diff --git a/lib/formdata.c b/lib/formdata.c index 3619c15bb121..681902db71e1 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -154,7 +154,7 @@ static void AddFormInfo(struct FormInfo *form_info, struct FormInfo *parent) static void free_formlist(struct FormInfo *ptr) { - for(; ptr != NULL; ptr = ptr->more) { + for(; ptr; ptr = ptr->more) { Curl_bufref_free(&ptr->name); Curl_bufref_free(&ptr->value); Curl_bufref_free(&ptr->contenttype); @@ -184,7 +184,7 @@ static void free_formlist(struct FormInfo *ptr) * * name/value pair where only the content pointer is remembered: * curl_formadd(&post, &last, CURLFORM_COPYNAME, "name", - * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, + * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10L, * CURLFORM_END); * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used) * @@ -223,7 +223,7 @@ 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) { + for(form = first_form; form; form = form->more) { const char *name = Curl_bufref_ptr(&form->name); if(((!name || !Curl_bufref_ptr(&form->value)) && !post) || diff --git a/lib/ftp-int.h b/lib/ftp-int.h index 68d26f332721..8d7e14127488 100644 --- a/lib/ftp-int.h +++ b/lib/ftp-int.h @@ -113,10 +113,10 @@ struct ftp_conn { 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 */ + 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 *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. */ diff --git a/lib/ftp.c b/lib/ftp.c index 9d3700df5f7b..05d17064a08b 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -355,7 +355,6 @@ static void close_secondarysocket(struct Curl_easy *data, { (void)ftpc; CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_CSTATE(ftpc)); - Curl_conn_close(data, SECONDARYSOCKET); Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET); } @@ -733,7 +732,7 @@ static CURLcode getftpresponse(struct Curl_easy *data, pp->pending_resp = FALSE; CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zu, ftpcode=%d", - result, *nreadp, *ftpcodep); + (int)result, *nreadp, *ftpcodep); return result; } @@ -743,7 +742,7 @@ static CURLcode ftp_state_user(struct Curl_easy *data, struct connectdata *conn) { CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s", - conn->user ? conn->user : ""); + Curl_creds_user(conn->creds)); if(!result) { ftpc->ftp_trying_alternative = FALSE; ftp_state(data, ftpc, FTP_USER); @@ -1084,7 +1083,7 @@ static CURLcode ftp_port_open_socket(struct Curl_easy *data, curl_socket_t *portsockp) { char buffer[STRERROR_LEN]; - int error = 0; + int sockerr = 0; const struct Curl_addrinfo *ai; CURLcode result = CURLE_FTP_PORT_FAILED; @@ -1096,14 +1095,14 @@ static CURLcode ftp_port_open_socket(struct Curl_easy *data, if(result == CURLE_OUT_OF_MEMORY) return result; result = CURLE_FTP_PORT_FAILED; - error = SOCKERRNO; + sockerr = SOCKERRNO; continue; } break; } if(!ai) { failf(data, "socket failure: %s", - curlx_strerror(error, buffer, sizeof(buffer))); + curlx_strerror(sockerr, buffer, sizeof(buffer))); return CURLE_FTP_PORT_FAILED; } *aip = ai; @@ -1132,7 +1131,7 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, #endif char buffer[STRERROR_LEN]; unsigned short port; - int error; + int sockerr; memcpy(sa, ai->ai_addr, ai->ai_addrlen); *sslen_io = ai->ai_addrlen; @@ -1145,13 +1144,13 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, sa6->sin6_port = htons(port); #endif if(bind(portsock, sa, *sslen_io)) { - error = SOCKERRNO; - if(non_local && (error == SOCKEADDRNOTAVAIL)) { + sockerr = SOCKERRNO; + if(non_local && (sockerr == SOCKEADDRNOTAVAIL)) { /* The requested bind address is not local. Use the address used for * the control connection instead and restart the port loop. */ infof(data, "bind(port=%hu) on non-local address failed: %s", port, - curlx_strerror(error, buffer, sizeof(buffer))); + curlx_strerror(sockerr, buffer, sizeof(buffer))); *sslen_io = sizeof(*ss); if(getsockname(conn->sock[FIRSTSOCKET], sa, sslen_io)) { @@ -1163,9 +1162,9 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, non_local = FALSE; /* do not try this again */ continue; } - if(error != SOCKEADDRINUSE && error != SOCKEACCES) { + if(sockerr != SOCKEADDRINUSE && sockerr != SOCKEACCES) { failf(data, "bind(port=%hu) failed: %s", port, - curlx_strerror(error, buffer, sizeof(buffer))); + curlx_strerror(sockerr, buffer, sizeof(buffer))); return CURLE_FTP_PORT_FAILED; } } @@ -1188,8 +1187,7 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data, curlx_strerror(SOCKERRNO, buffer, sizeof(buffer))); return CURLE_FTP_PORT_FAILED; } - CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", - port); + CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", port); return CURLE_OK; } @@ -1276,8 +1274,7 @@ static CURLcode ftp_port_send_command(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); + sa->sa_family == AF_INET ? 1 : 2, myhost, port); if(result) { failf(data, "Failure sending EPRT command: %s", curl_easy_strerror(result)); @@ -1393,10 +1390,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, ftp_state(data, ftpc, FTP_STOP); } else { - /* successfully set up the listen socket filter. SSL needed? */ + /* successfully set up the listen socket filter. SSL needed? + * Use the control connections origin for cert verification. */ 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); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), + conn, SECONDARYSOCKET); } conn->bits.do_more = FALSE; Curl_pgrsTime(data, TIMER_STARTACCEPT); @@ -1983,11 +1983,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, { CURLcode result = CURLE_OK; - if(conn->bits.ipv6 -#ifndef CURL_DISABLE_PROXY - && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) -#endif - ) { + if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) { /* We cannot disable EPSV when doing IPv6, so this is instead a fail */ failf(data, "Failed EPSV attempt, exiting"); return CURLE_WEIRD_SERVER_REPLY; @@ -2019,8 +2015,8 @@ static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp) the effective control connection address is the proxy address, not the ftp host. */ #ifndef CURL_DISABLE_PROXY - if(conn->bits.tunnel_proxy || conn->bits.socksproxy) - *newhostp = curlx_strdup(conn->host.name); + if(Curl_conn_is_tunneling(conn, FIRSTSOCKET)) + *newhostp = curlx_strdup(conn->origin->hostname); else #endif if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) && @@ -2059,8 +2055,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, { struct connectdata *conn = data->conn; CURLcode result; - struct Curl_dns_entry *dns = NULL; - unsigned short connectport; /* the local port connect() should use! */ const struct pingpong *pp = &ftpc->pp; char *newhost = NULL; unsigned short newport = 0; @@ -2075,7 +2069,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, ptr++; /* |||12345| */ sep = ptr[0]; - if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { + if(sep && (ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) { const char *p = &ptr[3]; curl_off_t num; if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) { @@ -2125,7 +2119,7 @@ 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); + ip[0], ip[1], ip[2], ip[3], conn->origin->hostname); result = ftp_control_addr_dup(data, &newhost); if(result) return result; @@ -2147,67 +2141,25 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, return CURLE_FTP_WEIRD_PASV_REPLY; } -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy) { - /* This connection uses a proxy and we need to connect to the proxy again - * here. We do not want to rely on a former host lookup that might have - * expired now, instead we remake the lookup here and now! */ - struct ip_quadruple ipquad; - bool is_ipv6; - const char * const host_name = conn->bits.socksproxy ? - conn->socks_proxy.host.name : conn->http_proxy.host.name; - - result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET, - &is_ipv6, &ipquad); - if(result) - goto error; - - (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); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; - } - } - else -#endif - { - /* normal, direct, ftp connection */ - DEBUGASSERT(newhost); - - /* postponed address resolution in case of tcp fastopen */ - if(conn->bits.tcp_fastopen && !conn->bits.reuse && !newhost[0]) { - curlx_free(newhost); - result = ftp_control_addr_dup(data, &newhost); - if(result) - goto error; - } - - (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 */ + DEBUGASSERT(newhost); + Curl_peer_unlink(&conn->origin2); + result = Curl_peer_create(data, conn->scheme, newhost, newport, + &conn->origin2); + if(result) + goto error; - if(!dns) { - failf(data, "cannot resolve new host %s:%hu", newhost, connectport); - result = CURLE_FTP_CANT_GET_HOST; + /* If FIRSTSOCKET goes via another peer, SECONDARY needs as well, + * but with its new port. */ + if(conn->via_peer) { + Curl_peer_unlink(&conn->via_peer2); + result = Curl_peer_create(data, conn->via_peer->scheme, + conn->via_peer->hostname, newport, + &conn->via_peer2); + if(result) 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, + result = Curl_conn_setup(data, conn, SECONDARYSOCKET, conn->bits.ftp_use_data_ssl ? CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); @@ -2227,13 +2179,10 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, #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); + /* Dump information about this second connection when we have issued + * a PASV command. */ + infof(data, "Connecting to %s port %d", + conn->origin2->hostname, conn->origin2->port); } #endif @@ -2241,7 +2190,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data, ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */ error: - Curl_dns_entry_unlink(data, &dns); curlx_free(newhost); return result; } @@ -2267,10 +2215,10 @@ static CURLcode ftp_statemach(struct Curl_easy *data, * 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 + * 'more' can return DOMORE_INCOMPLETE, DOMORE_DONE or DOMORE_GOBACK * (which is for when PASV is being sent to retry a failed EPSV). */ -static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) +static CURLcode ftp_do_more(struct Curl_easy *data, domore *more) { struct connectdata *conn = data->conn; struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); @@ -2285,7 +2233,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(!ftpc || !ftp) return CURLE_FAILED_INIT; - *completep = 0; /* default to stay in the state */ + *more = DOMORE_INCOMPLETE; /* 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 @@ -2303,7 +2251,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) 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 */ + *more = DOMORE_GOBACK; /* go back to DOING please */ /* this is a EPSV connect failing, try PASV instead */ return ftp_epsv_disable(data, ftpc, conn); } @@ -2316,7 +2264,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) They are only done to kickstart the do_more state */ result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; + if(complete) + *more = DOMORE_DONE; /* if we got an error or if we do not wait for a data connection return immediately */ @@ -2326,7 +2275,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) /* 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; + *more = DOMORE_INCOMPLETE; } if(ftp->transfer <= PPTRANSFER_INFO) { @@ -2348,8 +2297,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(result) return result; - *completep = 1; /* this state is now complete when the server has - connected back to us */ + *more = DOMORE_DONE; /* this state is now complete when the server has + connected back to us */ } else { result = ftp_check_ctrl_on_data_wait(data, ftpc); @@ -2368,7 +2317,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) * 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); + *more = (!ftpc->wait_data_conn && complete) ? + DOMORE_DONE : DOMORE_INCOMPLETE; } else { /* download */ @@ -2411,7 +2361,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) } result = ftp_statemach(data, ftpc, &complete); - *completep = (int)complete; + if(complete) + *more = DOMORE_DONE; } return result; } @@ -2421,7 +2372,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(!ftpc->wait_data_conn) { /* no waiting for the data connection so this is now complete */ - *completep = 1; + *more = DOMORE_DONE; CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc), (int)result); } @@ -2436,7 +2387,7 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected) { if(connected) { - int completed; + domore completed; CURLcode result = ftp_do_more(data, &completed); if(result) { @@ -2950,7 +2901,7 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data, { CURLcode result = CURLE_OK; - if(data->conn->bits.ftp_use_control_ssl) { + if(Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { /* PBSZ = PROTECTION BUFFER SIZE. The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says: @@ -2986,7 +2937,8 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data, 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); + result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", + Curl_creds_passwd(data->conn->creds)); if(!result) ftp_state(data, ftpc, FTP_PASS); } @@ -3161,8 +3113,8 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, CURLcode result = CURLE_OK; if(ftpcode == 230) { /* 230 User logged in - already! Take as 220 if TLS required. */ - if(data->set.use_ssl <= CURLUSESSL_TRY || - conn->bits.ftp_use_control_ssl) + if(ftpc->use_ssl <= CURLUSESSL_TRY || + Curl_conn_is_ssl(conn, FIRSTSOCKET)) return ftp_state_user_resp(data, ftpc, ftpcode); } else if(ftpcode != 220) { @@ -3171,7 +3123,7 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data, return CURLE_WEIRD_SERVER_REPLY; } - if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) { + if(ftpc->use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { /* We do not have an SSL/TLS control connection yet, but FTPS is requested. Try an FTPS connection now */ @@ -3243,16 +3195,17 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* this was BLOCKING, keep it so for now */ bool done; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) { /* we failed and bail out */ return CURLE_USE_SSL_FAILED; } } + /* BLOCKING */ result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done); if(!result) { conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */ - conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */ result = ftp_state_user(data, ftpc, conn); } } @@ -3264,7 +3217,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* remain in this same state */ } else { - if(data->set.use_ssl > CURLUSESSL_TRY) + if(ftpc->use_ssl > CURLUSESSL_TRY) /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */ result = CURLE_USE_SSL_FAILED; else @@ -3285,7 +3238,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_PBSZ: result = Curl_pp_sendf(data, &ftpc->pp, "PROT %c", - data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); + ftpc->use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P'); if(!result) ftp_state(data, ftpc, FTP_PROT); break; @@ -3293,10 +3246,10 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_PROT: if(ftpcode / 100 == 2) /* We have enabled SSL for the data connection! */ - conn->bits.ftp_use_data_ssl = (data->set.use_ssl != CURLUSESSL_CONTROL); + conn->bits.ftp_use_data_ssl = (ftpc->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) + else if(ftpc->use_ssl > CURLUSESSL_CONTROL) /* we failed and bails out */ return CURLE_USE_SSL_FAILED; @@ -3572,7 +3525,6 @@ static CURLcode ftp_connect(struct Curl_easy *data, result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done); if(result) return result; - conn->bits.ftp_use_control_ssl = TRUE; } Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */ @@ -3641,30 +3593,10 @@ static CURLcode ftp_sendquote(struct Curl_easy *data, return CURLE_OK; } -/*********************************************************************** - * - * ftp_done() - * - * The DONE function. This does what needs to be done after a single DO has - * performed. - * - * Input argument is already checked for validity. - */ -static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, - bool premature) +static CURLcode ftp_done_status(struct connectdata *conn, + struct ftp_conn *ftpc, CURLcode status, + bool premature) { - struct connectdata *conn = data->conn; - struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); - struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); - struct pingpong *pp; - size_t nread; - int ftpcode; - CURLcode result = CURLE_OK; - - if(!ftp || !ftpc) - return CURLE_OK; - - pp = &ftpc->pp; switch(status) { case CURLE_BAD_DOWNLOAD_RESUME: case CURLE_FTP_WEIRD_PASV_REPLY: @@ -3693,10 +3625,13 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the current path, as this connection is going */ connclose(conn, "FTP ended with bad error code"); - result = status; /* use the already set error code */ - break; + return status; /* use the already set error code */ } + return CURLE_OK; +} +static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc) +{ if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { Curl_set_in_callback(data, TRUE); @@ -3706,14 +3641,18 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } ftpc->known_filesize = -1; } +} +static void ftp_done_path(struct Curl_easy *data, struct ftp_conn *ftpc, + CURLcode result) +{ + struct connectdata *conn = data->conn; if(result) { /* We can limp along anyway (and should try to since we may already be in * the error path) */ ftpc->ctl_valid = FALSE; /* mark control connection as bad */ connclose(conn, "FTP: out of memory!"); /* mark for connection closure */ - curlx_free(ftpc->prevpath); - ftpc->prevpath = NULL; /* no path remembering */ + curlx_safefree(ftpc->prevpath); /* no path remembering */ } else { /* remember working directory for connection reuse */ const char *rawPath = ftpc->rawpath; @@ -3740,13 +3679,17 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, if(ftpc->prevpath) infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath); } +} - /* shut down the socket to inform the server we are done */ - +static CURLcode ftp_done_secondary_socket(struct Curl_easy *data, + struct ftp_conn *ftpc, + CURLcode result) +{ + struct connectdata *conn = data->conn; if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) { if(!result && ftpc->dont_check && data->req.maxdownload > 0) { /* partial download completed */ - result = Curl_pp_sendf(data, pp, "%s", "ABOR"); + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "ABOR"); if(result) { failf(data, "Failure sending ABOR command: %s", curl_easy_strerror(result)); @@ -3757,16 +3700,27 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, close_secondarysocket(data, ftpc); } + return result; +} + +static CURLcode ftp_done_control_reply(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, CURLcode result, + bool premature) +{ + struct connectdata *conn = data->conn; + size_t nread; + int ftpcode; if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid && - pp->pending_resp && !premature) { + ftpc->pp.pending_resp && !premature) { /* * Let's see what the server says about the transfer we performed, but * lower the timeout as sometimes this connection has died while the data * has been transferred. This happens when doing through NATs etc that * abandon old silent connections. */ - pp->response = *Curl_pgrs_now(data); /* timeout relative now */ + ftpc->pp.response = *Curl_pgrs_now(data); /* timeout relative now */ result = getftpresponse(data, &nread, &ftpcode); if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) { @@ -3803,7 +3757,14 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, } } } + return result; +} +static CURLcode ftp_done_check_partial(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, CURLcode result, + bool premature) +{ if(result || premature) /* the response code from the transfer showed an error already so no use checking further */ @@ -3837,6 +3798,35 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, result = CURLE_FTP_COULDNT_RETR_FILE; } } + return result; +} + +/*********************************************************************** + * + * ftp_done() + * + * The DONE function. This does what needs to be done after a single DO has + * performed. + * + * Input argument is already checked for validity. + */ +static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, + bool premature) +{ + struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY); + struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN); + CURLcode result; + + if(!ftp || !ftpc) + return CURLE_OK; + + result = ftp_done_status(data->conn, ftpc, status, premature); + + ftp_done_wildcard(data, ftpc); + ftp_done_path(data, ftpc, result); + result = ftp_done_secondary_socket(data, ftpc, result); + result = ftp_done_control_reply(data, ftpc, ftp, result, premature); + result = ftp_done_check_partial(data, ftpc, ftp, result, premature); /* clear these for next connection */ ftp->transfer = PPTRANSFER_BODY; @@ -3845,7 +3835,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status, /* Send any post-transfer QUOTE strings? */ if(!status && !result && !premature && data->set.postquote) result = ftp_sendquote(data, ftpc, data->set.postquote); - CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), result); + CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), (int)result); return result; } @@ -4454,7 +4444,8 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data, ftpc->use_ssl = data->set.use_ssl; ftpc->ccc = data->set.ftp_ccc; - CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), result); + CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), + (int)result); return result; } @@ -4462,14 +4453,22 @@ bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn) { struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN); struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */ + /* Also match ACCOUNT, ALTERNATIVE-TO-USER and CCC options */ if(!nftpc || !cftpc || Curl_timestrcmp(nftpc->account, cftpc->account) || Curl_timestrcmp(nftpc->alternative_to_user, cftpc->alternative_to_user) || - (nftpc->use_ssl != cftpc->use_ssl) || (nftpc->ccc != cftpc->ccc)) return FALSE; + /* A mismatch on `use_ssl` MUST have been found in connection matching + * before we come here. This is a check on MAYBE/MUST use of STARTTLS and + * it only works on FTP. But IMAP/SMTP etc have the same `use_ssl` and + * no extra match like FTP. We lack tests in this area, so let FTP fail + * loudly here to help other cases. */ + if(nftpc->use_ssl > cftpc->use_ssl) { + DEBUGASSERT(0); + return FALSE; + } return TRUE; } diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index b5d9338c1bc2..23fbd1f07c40 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -196,13 +196,11 @@ void Curl_wildcard_dtor(struct WildcardData **wcp) wc->dtor = ZERO_NULL; wc->ftpwc = NULL; } - DEBUGASSERT(wc->ftpwc == NULL); + DEBUGASSERT(!wc->ftpwc); Curl_llist_destroy(&wc->filelist, NULL); - curlx_free(wc->path); - wc->path = NULL; - curlx_free(wc->pattern); - wc->pattern = NULL; + curlx_safefree(wc->path); + curlx_safefree(wc->pattern); wc->state = CURLWC_INIT; curlx_free(wc); *wcp = NULL; @@ -312,8 +310,9 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, str + parser->offsets.group : NULL; finfo->strings.perm = parser->offsets.perm ? str + parser->offsets.perm : NULL; - finfo->strings.target = parser->offsets.symlink_target ? - str + parser->offsets.symlink_target : NULL; + finfo->strings.target = parser->offsets.symlink_target && + (finfo->filetype == CURLFILETYPE_SYMLINK) ? + str + parser->offsets.symlink_target : NULL; finfo->strings.time = str + parser->offsets.time; finfo->strings.user = parser->offsets.user ? str + parser->offsets.user : NULL; @@ -940,7 +939,7 @@ static CURLcode parse_winnt(struct Curl_easy *data, parser->item_length++; if(c == ' ') { mem[parser->item_offset + parser->item_length - 1] = 0; - if(strcmp("

", mem + parser->item_offset) == 0) { + if(!strcmp("", mem + parser->item_offset)) { finfo->filetype = CURLFILETYPE_DIRECTORY; finfo->size = 0; } diff --git a/lib/functypes.h b/lib/functypes.h index 887c2612ef94..3938295dd1f7 100644 --- a/lib/functypes.h +++ b/lib/functypes.h @@ -48,7 +48,7 @@ #define SEND_TYPE_ARG3 int #define SEND_TYPE_RETV int -#elif defined(__AMIGA__) /* Any AmigaOS flavour */ +#elif defined(__AMIGA__) /* Any AmigaOS flavor */ /* long recv(long, char *, long, long); */ #define RECV_TYPE_ARG1 long diff --git a/lib/getinfo.c b/lib/getinfo.c index fab63e669a46..fde4aa4ef2d3 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -67,11 +67,8 @@ void Curl_initinfo(struct Curl_easy *data) info->httpauthpicked = 0; info->numconnects = 0; - curlx_free(info->contenttype); - info->contenttype = NULL; - - curlx_free(info->wouldredirect); - info->wouldredirect = NULL; + curlx_safefree(info->contenttype); + curlx_safefree(info->wouldredirect); memset(&info->primary, 0, sizeof(info->primary)); info->retry_after = 0; diff --git a/lib/gopher.c b/lib/gopher.c index f087121d0784..039697466b73 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -133,7 +133,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done) if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - /* Do not busyloop. The entire loop thing is a work-around as it causes a + /* Do not busyloop. The entire loop thing is a workaround as it causes a BLOCKING behavior which is a NO-NO. This function should rather be split up in a do and a doing piece where the pieces that are not possible to send now will be sent in the doing function repeatedly diff --git a/lib/headers.c b/lib/headers.c index 195e12b371a0..b290a9e5b340 100644 --- a/lib/headers.c +++ b/lib/headers.c @@ -305,7 +305,7 @@ static CURLcode hds_cw_collect_write(struct Curl_easy *data, CURLH_HEADER))); CURLcode result = Curl_headers_push(data, buf, blen, htype); CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d", - htype, blen, result); + htype, blen, (int)result); if(result) return result; } diff --git a/lib/hostip.c b/lib/hostip.c index 85f53c4ef4a4..a18d9a62bb9d 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -70,10 +70,6 @@ #define USE_ALARM_TIMEOUT #endif -#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ - -#define MAX_DNS_CACHE_SIZE 29999 - #define RESOLV_FAIL(for_proxy) \ ((for_proxy) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST) @@ -511,7 +507,6 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, return Curl_async_get_ai(data, async, ai_family, index); } - #ifdef USE_HTTPSRR const struct Curl_https_rrinfo * Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) @@ -529,7 +524,6 @@ bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) return TRUE; return Curl_async_knows_https(data, async); } - #endif /* USE_HTTPSRR */ #endif /* USE_CURL_ASYNC */ @@ -740,7 +734,7 @@ static CURLcode hostip_resolv(struct Curl_easy *data, failf(data, "Could not resolve: %s:%u", hostname, port); } else { - failf(data, "Error %d resolving %s:%u", result, hostname, port); + failf(data, "Error %d resolving %s:%u", (int)result, hostname, port); } } else if(cache_dns && *pdns) { @@ -795,9 +789,6 @@ CURL_NORETURN static void alarmfunc(int sig) (void)sig; siglongjmp(curl_jmpenv, 1); } -#endif /* USE_ALARM_TIMEOUT */ - -#ifdef USE_ALARM_TIMEOUT static CURLcode resolv_alarm_timeout(struct Curl_easy *data, uint8_t dns_queries, @@ -935,6 +926,33 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, #endif /* USE_ALARM_TIMEOUT */ +#ifdef USE_UNIX_SOCKETS +static CURLcode resolv_unix(struct Curl_easy *data, + const char *unix_path, + bool abstract_path, + struct Curl_dns_entry **pdns) +{ + 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 /* USE_UNIX_SOCKETS */ + /* * 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 @@ -956,16 +974,14 @@ static CURLcode resolv_alarm_timeout(struct Curl_easy *data, * any other CURLcode error, *pdns == NULL */ CURLcode Curl_resolv(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, bool for_proxy, timediff_t timeout_ms, uint32_t *presolv_id, struct Curl_dns_entry **pdns) { - DEBUGASSERT(hostname && *hostname); *presolv_id = 0; *pdns = NULL; @@ -975,14 +991,23 @@ CURLcode Curl_resolv(struct Curl_easy *data, else if(!timeout_ms) timeout_ms = CURL_TIMEOUT_RESOLVE_MS; +#ifdef USE_UNIX_SOCKETS + if(peer->unix_socket) + return resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, pdns); +#else + if(peer->unix_socket) + return hostip_resolv_failed(data, peer->hostname, for_proxy); +#endif + #ifdef USE_ALARM_TIMEOUT if(timeout_ms && data->set.no_signal) { /* Cannot use ALARM when signals are disabled */ timeout_ms = 0; } if(timeout_ms && !Curl_doh_wanted(data)) { - return resolv_alarm_timeout(data, dns_queries, hostname, port, transport, - for_proxy, timeout_ms, presolv_id, pdns); + return resolv_alarm_timeout(data, dns_queries, peer->hostname, peer->port, + transport, for_proxy, timeout_ms, presolv_id, + pdns); } #endif /* !USE_ALARM_TIMEOUT */ @@ -991,8 +1016,9 @@ CURLcode Curl_resolv(struct Curl_easy *data, infof(data, "timeout on name lookup is not supported"); #endif - return hostip_resolv(data, dns_queries, hostname, port, transport, - for_proxy, timeout_ms, TRUE, presolv_id, pdns); + return hostip_resolv(data, dns_queries, peer->hostname, peer->port, + transport, for_proxy, timeout_ms, TRUE, presolv_id, + pdns); } #ifdef USE_CURL_ASYNC @@ -1047,7 +1073,7 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, } else if(result) { failf(data, "Error %d resolving %s:%u", - result, async->hostname, async->port); + (int)result, async->hostname, async->port); } return result; } @@ -1101,30 +1127,3 @@ void Curl_resolv_destroy_all(struct Curl_easy *data) } #endif /* USE_CURL_ASYNC */ - -#ifdef USE_UNIX_SOCKETS -CURLcode Curl_resolv_unix(struct Curl_easy *data, - const char *unix_path, - bool abstract_path, - struct Curl_dns_entry **pdns) -{ - 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 /* USE_UNIX_SOCKETS */ diff --git a/lib/hostip.h b/lib/hostip.h index 780fb4dc13df..45370ec48e0c 100644 --- a/lib/hostip.h +++ b/lib/hostip.h @@ -45,6 +45,7 @@ struct easy_pollset; struct Curl_https_rrinfo; struct Curl_multi; struct Curl_dns_entry; +struct Curl_peer; /* DNS query types */ #define CURL_DNSQ_A (1U << 0) @@ -96,9 +97,8 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. */ CURLcode Curl_resolv(struct Curl_easy *data, + struct Curl_peer *peer, uint8_t dns_queries, - const char *hostname, - uint16_t port, uint8_t transport, bool for_proxy, timediff_t timeout_ms, @@ -184,11 +184,4 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, uint8_t transport); #endif -#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 fb35e3992ca4..c8cc4dc340a0 100644 --- a/lib/hostip4.c +++ b/lib/hostip4.c @@ -64,7 +64,7 @@ * the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or * HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME * has the corresponding rules. This is primarily on *nix. Note that some Unix - * flavours have thread-safe versions of the plain gethostbyname() etc. + * flavors have thread-safe versions of the plain gethostbyname() etc. * */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, diff --git a/lib/hsts.c b/lib/hsts.c index 400b4423da14..b8745a5195e4 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -96,7 +96,7 @@ void Curl_hsts_cleanup(struct hsts **hp) 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 */ + /* It is 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); @@ -121,7 +121,7 @@ static CURLcode hsts_create(struct hsts *h, struct stsentry *sts = curlx_calloc(1, sizeof(struct stsentry) + hlen); if(!sts) return CURLE_OUT_OF_MEMORY; - /* the null terminator is already there */ + /* the null-terminator is already there */ memcpy(sts->host, hostname, hlen); sts->expires = expires; sts->includeSubDomains = subdomains; @@ -130,6 +130,81 @@ static CURLcode hsts_create(struct hsts *h, return CURLE_OK; } +/* Copy all live entries from src into dst. Used by curl_easy_duphandle so the + * clone inherits entries learned at runtime. E.g. Strict-Transport-Security. + */ +CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src) +{ + struct Curl_llist_node *e; + time_t now = time(NULL); + for(e = Curl_llist_head(&src->list); e; e = Curl_node_next(e)) { + struct stsentry *sts = Curl_node_elem(e); + if(sts->expires > now) { + CURLcode result = hsts_create(dst, sts->host, strlen(sts->host), + sts->includeSubDomains != 0, sts->expires); + if(result) + return result; + } + } + return CURLE_OK; +} + +/* + * Return the matching HSTS entry, or NULL if the given hostname is not + * currently an HSTS one. + * + * The 'subdomain' argument tells the function if subdomain matching should be + * attempted. + * + * @unittest 1660 + */ +UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname, + size_t hlen, bool subdomain); +UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname, + size_t hlen, bool subdomain) +{ + struct stsentry *bestsub = NULL; + if(h) { + time_t now = time(NULL); + struct Curl_llist_node *e; + struct Curl_llist_node *n; + size_t blen = 0; + + if((hlen > MAX_HSTS_HOSTLEN) || !hlen) + return NULL; + if(hostname[hlen - 1] == '.') + /* remove the trailing dot */ + --hlen; + + for(e = Curl_llist_head(&h->list); e; e = n) { + struct stsentry *sts = Curl_node_elem(e); + size_t ntail; + n = Curl_node_next(e); + if(sts->expires <= now) { + /* remove expired entries */ + Curl_node_remove(&sts->node); + hsts_free(sts); + continue; + } + ntail = strlen(sts->host); + if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { + size_t offs = hlen - ntail; + if((hostname[offs - 1] == '.') && + curl_strnequal(&hostname[offs], sts->host, ntail) && + (ntail > blen)) { + /* save the tail match with the longest tail */ + bestsub = sts; + blen = ntail; + } + } + /* avoid curl_strequal because the hostname is not null-terminated */ + if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) + return sts; + } + } + return bestsub; +} + CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, const char *header) { @@ -203,7 +278,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, if(!expires) { /* remove the entry if present verbatim (without subdomain match) */ - sts = Curl_hsts(h, hostname, hlen, FALSE); + sts = hsts_check(h, hostname, hlen, FALSE); if(sts) { Curl_node_remove(&sts->node); hsts_free(sts); @@ -218,7 +293,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, expires += now; /* check if it already exists */ - sts = Curl_hsts(h, hostname, hlen, FALSE); + sts = hsts_check(h, hostname, hlen, FALSE); if(sts) { /* update these fields */ sts->expires = expires; @@ -230,57 +305,6 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, return CURLE_OK; } -/* - * Return TRUE if the given hostname is currently an HSTS one. - * - * The 'subdomain' argument tells the function if subdomain matching should be - * attempted. - */ -struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, - size_t hlen, bool subdomain) -{ - struct stsentry *bestsub = NULL; - if(h) { - time_t now = time(NULL); - struct Curl_llist_node *e; - struct Curl_llist_node *n; - size_t blen = 0; - - if((hlen > MAX_HSTS_HOSTLEN) || !hlen) - return NULL; - if(hostname[hlen - 1] == '.') - /* remove the trailing dot */ - --hlen; - - for(e = Curl_llist_head(&h->list); e; e = n) { - struct stsentry *sts = Curl_node_elem(e); - size_t ntail; - n = Curl_node_next(e); - if(sts->expires <= now) { - /* remove expired entries */ - Curl_node_remove(&sts->node); - hsts_free(sts); - continue; - } - ntail = strlen(sts->host); - if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { - size_t offs = hlen - ntail; - if((hostname[offs - 1] == '.') && - curl_strnequal(&hostname[offs], sts->host, ntail) && - (ntail > blen)) { - /* save the tail match with the longest tail */ - bestsub = sts; - blen = ntail; - } - } - /* avoid curl_strequal because the hostname is not null-terminated */ - if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) - return sts; - } - } - return bestsub; -} - /* * Send this HSTS entry to the write callback. */ @@ -437,10 +461,10 @@ static CURLcode hsts_add_host_expire(struct hsts *h, if(hostlen) { /* only add it if not already present */ - e = Curl_hsts(h, host, hostlen, subdomain); + e = hsts_check(h, host, hostlen, subdomain); if(!e) result = hsts_create(h, host, hostlen, subdomain, expires); - /* 'host' is not necessarily null terminated */ + /* '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 @@ -504,7 +528,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h) 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 */ + /* bail out if no name was stored or if a null-terminator is gone */ return CURLE_BAD_FUNCTION_ARGUMENT; if(!date[0]) date = UNLIMITED; @@ -610,6 +634,11 @@ CURLcode Curl_hsts_loadfiles(struct Curl_easy *data) return result; } +bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest) +{ + return !!hsts_check(h, dest->hostname, strlen(dest->hostname), TRUE); +} + #if defined(DEBUGBUILD) || defined(UNITTESTS) #undef time #endif diff --git a/lib/hsts.h b/lib/hsts.h index 0e6585f11606..08215f5eaab8 100644 --- a/lib/hsts.h +++ b/lib/hsts.h @@ -25,9 +25,13 @@ ***************************************************************************/ #include "curl_setup.h" +struct hsts; + #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) #include "llist.h" +struct Curl_peer; + #define MAX_HSTS_ENTRIES 10000 #if defined(DEBUGBUILD) || defined(UNITTESTS) @@ -50,10 +54,9 @@ struct hsts { struct hsts *Curl_hsts_init(void); void Curl_hsts_cleanup(struct hsts **hp); +CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src); CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, 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, const char *file); CURLcode Curl_hsts_loadfile(struct Curl_easy *data, @@ -61,6 +64,9 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data, CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h); CURLcode Curl_hsts_loadfiles(struct Curl_easy *data); + +bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest); + #else #define Curl_hsts_cleanup(x) #define Curl_hsts_loadcb(x, y) CURLE_OK diff --git a/lib/http.c b/lib/http.c index 9118c7e716ac..83d0e011528e 100644 --- a/lib/http.c +++ b/lib/http.c @@ -154,7 +154,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, { struct curl_slist *head; - for(head = (conn->bits.proxy && data->set.sep_headers) ? + for(head = (conn->http_proxy.peer && data->set.sep_headers) ? data->set.proxyheaders : data->set.headers; head; head = head->next) { if(curl_strnequal(head->data, thisheader, thislen) && @@ -250,34 +250,38 @@ char *Curl_copy_header_value(const char *header) * * Returns CURLcode. */ -static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) +static CURLcode http_output_basic(struct Curl_easy *data, + struct connectdata *conn, bool proxy) { size_t size = 0; char *authorization = NULL; - char **userp; - const char *user; - const char *pwd; + char **p_hd; CURLcode result; + struct Curl_creds *creds = NULL; char *out; /* credentials are unique per transfer for HTTP, do not use the ones for the connection */ if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = &data->req.proxyuserpwd; - user = data->state.aptr.proxyuser; - pwd = data->state.aptr.proxypasswd; + p_hd = &data->req.hd_proxy_auth; + creds = conn->http_proxy.creds; #else + (void)conn; return CURLE_NOT_BUILT_IN; #endif } else { - userp = &data->req.userpwd; - user = data->state.aptr.user; - pwd = data->state.aptr.passwd; + p_hd = &data->req.hd_auth; + creds = data->state.creds; + } + + if(!creds) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; } - out = curl_maprintf("%s:%s", user ? user : "", pwd ? pwd : ""); + out = curl_maprintf("%s:%s", creds->user, creds->passwd); if(!out) return CURLE_OUT_OF_MEMORY; @@ -291,12 +295,12 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy) goto fail; } - curlx_free(*userp); - *userp = curl_maprintf("%sAuthorization: Basic %s\r\n", - proxy ? "Proxy-" : "", - authorization); + curlx_free(*p_hd); + *p_hd = curl_maprintf("%sAuthorization: Basic %s\r\n", + proxy ? "Proxy-" : "", + authorization); curlx_free(authorization); - if(!*userp) { + if(!*p_hd) { result = CURLE_OUT_OF_MEMORY; goto fail; } @@ -320,10 +324,11 @@ static CURLcode http_output_bearer(struct Curl_easy *data) char **userp; CURLcode result = CURLE_OK; - userp = &data->req.userpwd; + DEBUGASSERT(Curl_creds_has_oauth_bearer(data->state.creds)); + userp = &data->req.hd_auth; curlx_free(*userp); *userp = curl_maprintf("Authorization: Bearer %s\r\n", - data->set.str[STRING_BEARER]); + Curl_creds_oauth_bearer(data->state.creds)); if(!*userp) { result = CURLE_OUT_OF_MEMORY; @@ -333,7 +338,6 @@ static CURLcode http_output_bearer(struct Curl_easy *data) fail: return result; } - #endif #endif @@ -343,8 +347,10 @@ static CURLcode http_output_bearer(struct Curl_easy *data) * * return TRUE if one was picked */ -static bool pickoneauth(struct auth *pick, unsigned long mask) +static bool pickoneauth(struct auth *pick, unsigned long mask, + struct Curl_creds *creds) { + bool have_user_pass = Curl_creds_has_user_or_pass(creds); bool picked; /* only deal with authentication we want */ unsigned long avail = pick->avail & pick->want & mask; @@ -352,20 +358,20 @@ static bool pickoneauth(struct auth *pick, unsigned long mask) /* The order of these checks is highly relevant, as this will be the order of preference in case of the existence of multiple accepted types. */ - if(avail & CURLAUTH_NEGOTIATE) + if(avail & CURLAUTH_NEGOTIATE) /* available on empty creds */ pick->picked = CURLAUTH_NEGOTIATE; #ifndef CURL_DISABLE_BEARER_AUTH - else if(avail & CURLAUTH_BEARER) + else if((avail & CURLAUTH_BEARER) && Curl_creds_has_oauth_bearer(creds)) pick->picked = CURLAUTH_BEARER; #endif #ifndef CURL_DISABLE_DIGEST_AUTH - else if(avail & CURLAUTH_DIGEST) + else if((avail & CURLAUTH_DIGEST) && have_user_pass) pick->picked = CURLAUTH_DIGEST; #endif else if(avail & CURLAUTH_NTLM) pick->picked = CURLAUTH_NTLM; #ifndef CURL_DISABLE_BASIC_AUTH - else if(avail & CURLAUTH_BASIC) + else if((avail & CURLAUTH_BASIC) && have_user_pass) pick->picked = CURLAUTH_BASIC; #endif #ifndef CURL_DISABLE_AWS @@ -527,10 +533,10 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode) * 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) + if((httpcode == 401) && !data->state.creds) return TRUE; #ifndef CURL_DISABLE_PROXY - if((httpcode == 407) && !data->conn->bits.proxy_user_passwd) + if((httpcode == 407) && !data->conn->http_proxy.creds) return TRUE; #endif @@ -551,7 +557,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) CURLcode result = CURLE_OK; unsigned long authmask = ~0UL; - if(!data->set.str[STRING_BEARER]) + if(!Curl_creds_has_oauth_bearer(data->state.creds)) authmask &= (unsigned long)~CURLAUTH_BEARER; if(100 <= data->req.httpcode && data->req.httpcode <= 199) @@ -561,10 +567,10 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) if(data->state.authproblem) return data->set.http_fail_on_error ? CURLE_HTTP_RETURNED_ERROR : CURLE_OK; - if((data->state.aptr.user || data->set.str[STRING_BEARER]) && + if(data->state.creds && ((data->req.httpcode == 401) || (data->req.authneg && data->req.httpcode < 300))) { - pickhost = pickoneauth(&data->state.authhost, authmask); + pickhost = pickoneauth(&data->state.authhost, authmask, data->state.creds); if(!pickhost) data->state.authproblem = TRUE; else @@ -578,11 +584,12 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data) } } #ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy_user_passwd && + if(conn->http_proxy.creds && ((data->req.httpcode == 407) || (data->req.authneg && data->req.httpcode < 300))) { pickproxy = pickoneauth(&data->state.authproxy, - authmask & ~CURLAUTH_BEARER); + authmask & ~CURLAUTH_BEARER, + conn->http_proxy.creds); if(!pickproxy) data->state.authproblem = TRUE; else @@ -694,14 +701,16 @@ static CURLcode output_auth_headers(struct Curl_easy *data, /* Basic */ if( #ifndef CURL_DISABLE_PROXY - (proxy && conn->bits.proxy_user_passwd && + (proxy && conn->http_proxy.creds && + Curl_creds_has_user_or_pass(conn->http_proxy.creds) && !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) || #endif - (!proxy && data->state.aptr.user && + (!proxy && data->state.creds && + Curl_creds_has_user_or_pass(data->state.creds) && !Curl_checkheaders(data, STRCONST("Authorization")))) { auth = "Basic"; - result = http_output_basic(data, proxy); + result = http_output_basic(data, conn, proxy); if(result) return result; } @@ -714,8 +723,7 @@ static CURLcode output_auth_headers(struct Curl_easy *data, #ifndef CURL_DISABLE_BEARER_AUTH if(authstatus->picked == CURLAUTH_BEARER) { /* Bearer */ - if(!proxy && data->set.str[STRING_BEARER] && - Curl_auth_allowed_to_host(data) && + if(!proxy && Curl_creds_has_oauth_bearer(data->state.creds) && !Curl_checkheaders(data, STRCONST("Authorization"))) { auth = "Bearer"; result = http_output_bearer(data); @@ -737,15 +745,15 @@ static CURLcode output_auth_headers(struct Curl_easy *data, data->info.httpauthpicked = authstatus->picked; infof(data, "%s auth using %s with user '%s'", proxy ? "Proxy" : "Server", auth, - proxy ? (data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : "") : - (data->state.aptr.user ? - data->state.aptr.user : "")); + proxy ? (conn->http_proxy.creds ? + conn->http_proxy.creds->user : "") : + (data->state.creds ? + data->state.creds->user : "")); #else (void)proxy; infof(data, "Server auth using %s with user '%s'", - auth, data->state.aptr.user ? - data->state.aptr.user : ""); + auth, data->state.creds ? + data->state.creds->user : ""); #endif authstatus->multipass = !authstatus->done; } @@ -760,53 +768,47 @@ static CURLcode output_auth_headers(struct Curl_easy *data, return result; } -/** - * Curl_http_output_auth() setups the authentication headers for the - * host/proxy and the correct authentication - * method. data->state.authdone is set to TRUE when authentication is - * done. - * - * @param conn all information about the current connection - * @param request pointer to the request keyword - * @param path pointer to the requested path; should include query part - * @param proxytunnel boolean if this is the request setting up a "proxy - * tunnel" - * - * @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 */ + const char *query, + bool is_connect) { CURLcode result = CURLE_OK; struct auth *authhost; struct auth *authproxy; + const char *path_and_query = path; + char *tmp_str = NULL; DEBUGASSERT(data); - authhost = &data->state.authhost; authproxy = &data->state.authproxy; if( #ifndef CURL_DISABLE_PROXY - (conn->bits.httpproxy && conn->bits.proxy_user_passwd) || + (!conn->http_proxy.peer || !conn->http_proxy.creds) && #endif - data->state.aptr.user || #ifdef USE_SPNEGO - authhost->want & CURLAUTH_NEGOTIATE || - authproxy->want & CURLAUTH_NEGOTIATE || + !(authhost->want & CURLAUTH_NEGOTIATE) && + !(authproxy->want & CURLAUTH_NEGOTIATE) && #endif - data->set.str[STRING_BEARER]) - /* continue please */; - else { + !data->state.creds) { + /* no authentication with no user or password */ authhost->done = TRUE; authproxy->done = TRUE; - return CURLE_OK; /* no authentication with no user or password */ + result = CURLE_OK; + goto out; + } + + if(query) { + tmp_str = curl_maprintf("%s?%s", path, query); + if(!tmp_str) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + path_and_query = tmp_str; } if(authhost->want && !authhost->picked) @@ -823,28 +825,25 @@ CURLcode 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 == (curl_bit)proxytunnel)) { - result = output_auth_headers(data, conn, authproxy, request, path, TRUE); + if(conn->bits.origin_is_proxy || is_connect) { + result = output_auth_headers(data, conn, authproxy, request, + path_and_query, TRUE); if(result) - return result; + goto out; } else #else - (void)proxytunnel; + (void)is_connect; #endif /* CURL_DISABLE_PROXY */ /* we have no proxy so let's pretend we are done authenticating with it */ authproxy->done = TRUE; - /* To prevent the user+password to get sent to other than the original host - due to a location-follow */ - if(Curl_auth_allowed_to_host(data) -#ifndef CURL_DISABLE_NETRC - || conn->bits.netrc -#endif - ) - result = output_auth_headers(data, conn, authhost, request, path, FALSE); + /* Either we have credentials for the origin we talk to or + performing authentication is allowed here */ + if(data->state.creds || Curl_auth_allowed_to_host(data)) + result = output_auth_headers(data, conn, authhost, request, + path_and_query, FALSE); else authhost->done = TRUE; @@ -859,27 +858,31 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, else data->req.authneg = FALSE; +out: + curlx_free(tmp_str); return result; } -#else +#else /* !CURL_DISABLE_HTTP_AUTH */ /* 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) + const char *query, + bool is_connect) { (void)data; (void)conn; (void)request; (void)httpreq; (void)path; - (void)proxytunnel; + (void)query; + (void)is_connect; return CURLE_OK; } -#endif +#endif /* !CURL_DISABLE_HTTP_AUTH, else */ #if defined(USE_SPNEGO) || defined(USE_NTLM) || \ !defined(CURL_DISABLE_DIGEST_AUTH) || \ @@ -1134,9 +1137,9 @@ 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 || \ +#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, @@ -1227,8 +1230,6 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, return CURLE_OUT_OF_MEMORY; } else { - bool same_origin; - CURLcode result; CURLU *u = curl_url(); if(!u) return CURLE_OUT_OF_MEMORY; @@ -1242,29 +1243,16 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, return Curl_uc_to_curlcode(uc); } - same_origin = Curl_url_same_origin(u, data->state.uh); - curl_url_cleanup(u); - #ifndef CURL_DISABLE_DIGEST_AUTH - if(!same_origin) - Curl_auth_digest_cleanup(&data->state.digest); -#endif - - if((!same_origin && !data->set.allow_auth_to_other_hosts) || - !data->set.str[STRING_USERNAME]) { - 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); - } - result = Curl_reset_proxypwd(data); - if(result) { - curlx_free(follow_url); - return result; + { + bool same_origin = Curl_url_same_origin(u, data->state.uh); + curl_url_cleanup(u); + if(!same_origin) + Curl_auth_digest_cleanup(&data->state.digest); } +#else + curl_url_cleanup(u); +#endif } DEBUGASSERT(follow_url); @@ -1753,8 +1741,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, if(is_connect) proxy = HEADER_CONNECT; else - proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ? - HEADER_PROXY : HEADER_SERVER; + proxy = data->conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER; switch(proxy) { case HEADER_SERVER: @@ -1773,6 +1760,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, else h[0] = data->set.headers; break; + case HEADER_CONNECT_UDP: + if(data->set.sep_headers) + h[0] = data->set.proxyheaders; + else + h[0] = data->set.headers; + break; } #else (void)is_connect; @@ -1992,10 +1985,8 @@ static CURLcode http_useragent(struct Curl_easy *data) 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"))) { - curlx_free(data->state.aptr.uagent); - data->state.aptr.uagent = NULL; - } + if(Curl_checkheaders(data, STRCONST("User-Agent"))) + curlx_safefree(data->state.aptr.uagent); return CURLE_OK; } @@ -2005,25 +1996,15 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) struct dynamically_allocated_data *aptr = &data->state.aptr; const char *ptr; - if(!data->state.this_is_a_follow) { - /* Free to avoid leaking memory on multiple requests */ - curlx_free(data->state.first_host); - - 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->scheme->protocol; - } 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 || - curl_strequal(data->state.first_host, conn->host.name))) { + if(ptr && + (!data->state.this_is_a_follow || + Curl_peer_equal(data->state.initial_origin, data->state.origin))) { #ifndef CURL_DISABLE_COOKIES /* If we have a given custom Host: header, we extract the hostname in order to possibly use it for cookie reasons later on. We only allow the @@ -2067,18 +2048,19 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) } else { /* 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; + char *host = (data->state.origin->user_hostname[0] == '[') ? + data->state.origin->user_hostname : data->state.origin->hostname; if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && - (conn->remote_port == PORT_HTTPS)) || + (data->state.origin->port == PORT_HTTPS)) || ((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) && - (conn->remote_port == PORT_HTTP))) + (data->state.origin->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 = curl_maprintf("Host: %s\r\n", host); else - aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->remote_port); + aptr->host = curl_maprintf("Host: %s:%d\r\n", + host, data->state.origin->port); if(!aptr->host) /* without Host: we cannot make a nice request */ @@ -2106,7 +2088,7 @@ static CURLcode http_target(struct Curl_easy *data, } #ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + if(conn->bits.origin_is_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 @@ -2120,8 +2102,8 @@ static CURLcode http_target(struct Curl_easy *data, if(!h) return CURLE_OUT_OF_MEMORY; - if(conn->host.dispname != conn->host.name) { - uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); + if(data->state.origin->user_hostname != data->state.origin->hostname) { + uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0); if(uc) { curl_url_cleanup(h); return CURLE_OUT_OF_MEMORY; @@ -2146,6 +2128,19 @@ static CURLcode http_target(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } } + else if(data->state.creds && (data->state.creds->source != CREDS_URL)) { + /* credentials not from the URL need to be set */ + uc = curl_url_set(h, CURLUPART_USER, + data->state.creds->user, CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(h, CURLUPART_PASSWORD, + data->state.creds->passwd, CURLU_URLENCODE); + if(uc) { + curl_url_cleanup(h); + return Curl_uc_to_curlcode(uc); + } + } + /* Extract the URL to use in the request. */ uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT); if(uc) { @@ -2541,7 +2536,8 @@ static CURLcode http_cookies(struct Curl_easy *data, char *addcookies = NULL; bool linecap = FALSE; if(data->set.str[STRING_COOKIE] && - !Curl_checkheaders(data, STRCONST("Cookie"))) + !Curl_checkheaders(data, STRCONST("Cookie")) && + Curl_auth_allowed_to_host(data)) addcookies = data->set.str[STRING_COOKIE]; if(data->cookies || addcookies) { @@ -2551,9 +2547,9 @@ static CURLcode http_cookies(struct Curl_easy *data, if(data->cookies && data->state.cookie_engine) { bool okay; const char *host = data->req.cookiehost ? - data->req.cookiehost : data->conn->host.name; + data->req.cookiehost : data->state.origin->hostname; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - result = Curl_cookie_getlist(data, data->conn, &okay, host, &list); + result = Curl_cookie_getlist(data, &okay, host, &list); if(!result && okay) { struct Curl_llist_node *n; size_t clen = 8; /* hold the size of the generated Cookie: header */ @@ -2737,13 +2733,16 @@ static CURLcode http_check_new_conn(struct Curl_easy *data) alpn = Curl_conn_get_alpn_negotiated(data, conn); if(alpn && !strcmp("h3", alpn)) { - DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); +#ifndef CURL_DISABLE_PROXY + if(!conn->bits.origin_is_proxy) +#endif + DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); info_version = "HTTP/3"; } else if(alpn && !strcmp("h2", alpn)) { #ifndef CURL_DISABLE_PROXY if((Curl_conn_http_version(data, conn) != 20) && - conn->bits.proxy && !conn->bits.tunnel_proxy) { + conn->bits.origin_is_proxy) { result = Curl_http2_switch(data); if(result) return result; @@ -2842,7 +2841,7 @@ typedef enum { #ifndef CURL_DISABLE_PROXY H1_HD_PROXY_AUTH, #endif - H1_HD_USER_AUTH, + H1_HD_AUTH, H1_HD_RANGE, H1_HD_USER_AGENT, H1_HD_ACCEPT, @@ -2897,14 +2896,14 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_AUTH: - if(data->req.proxyuserpwd) - result = curlx_dyn_add(req, data->req.proxyuserpwd); + if(data->req.hd_proxy_auth) + result = curlx_dyn_add(req, data->req.hd_proxy_auth); break; #endif - case H1_HD_USER_AUTH: - if(data->req.userpwd) - result = curlx_dyn_add(req, data->req.userpwd); + case H1_HD_AUTH: + if(data->req.hd_auth) + result = curlx_dyn_add(req, data->req.hd_auth); break; case H1_HD_RANGE: @@ -2952,8 +2951,7 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_CONNECTION: - if(conn->bits.httpproxy && - !conn->bits.tunnel_proxy && + if(conn->bits.origin_is_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"); @@ -2966,10 +2964,10 @@ static CURLcode http_add_hd(struct Curl_easy *data, #ifndef CURL_DISABLE_ALTSVC case H1_HD_ALT_USED: - if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) + if(conn->bits.altused && conn->via_peer && + !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); + conn->via_peer->hostname, conn->via_peer->port); break; #endif @@ -3062,29 +3060,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* select host to send */ result = http_set_aptr_host(data); - if(!result) { - /* setup the authentication headers, how that method and host are known */ - char *pq = NULL; - if(data->state.up.query) { - pq = curl_maprintf("%s?%s", data->state.up.path, data->state.up.query); - if(!pq) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } + /* setup the authentication headers, how that method and host are known */ + if(!result) result = Curl_http_output_auth(data, data->conn, method, httpreq, - (pq ? pq : data->state.up.path), FALSE); - curlx_free(pq); - } - if(result) - goto out; - - result = http_useragent(data); - if(result) - goto out; - + data->state.up.path, + data->state.up.query, FALSE); + if(!result) + result = http_useragent(data); /* Setup input reader, resume information and ranges */ - result = set_reader(data, httpreq); + if(!result) + result = set_reader(data, httpreq); if(!result) result = http_resume(data, httpreq); if(!result) @@ -3209,7 +3194,6 @@ static CURLcode http_header_a(struct Curl_easy *data, { #ifndef CURL_DISABLE_ALTSVC const char *v; - struct connectdata *conn = data->conn; v = (data->asi && (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD @@ -3224,8 +3208,9 @@ static CURLcode http_header_a(struct Curl_easy *data, struct SingleRequest *k = &data->req; enum alpnid id = (k->httpversion == 30) ? ALPN_h3 : (k->httpversion == 20) ? ALPN_h2 : ALPN_h1; - return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name, - curlx_uitous((unsigned int)conn->remote_port)); + return Curl_altsvc_parse( + data, data->asi, v, id, data->state.origin->hostname, + curlx_uitous((unsigned int)data->state.origin->port)); } #else (void)data; @@ -3443,7 +3428,7 @@ static CURLcode http_header_p(struct Curl_easy *data, const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:"); if(v) { struct connectdata *conn = data->conn; - if((k->httpversion == 10) && conn->bits.httpproxy && + if((k->httpversion == 10) && conn->http_proxy.peer && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { /* * When an HTTP/1.0 reply comes when using a proxy, the @@ -3454,7 +3439,7 @@ static CURLcode http_header_p(struct Curl_easy *data, connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */ infof(data, "HTTP/1.0 proxy connection set to keep alive"); } - else if((k->httpversion == 11) && conn->bits.httpproxy && + else if((k->httpversion == 11) && conn->http_proxy.peer && HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { /* * We get an HTTP/1.1 response from a proxy and it says it will @@ -3537,7 +3522,6 @@ static CURLcode http_header_s(struct Curl_easy *data, const char *hd, size_t hdlen) { #if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_HSTS) - struct connectdata *conn = data->conn; const char *v; #else (void)data; @@ -3552,8 +3536,8 @@ static CURLcode http_header_s(struct Curl_easy *data, /* If there is a custom-set Host: name, use it here, or else use * real peer hostname. */ const char *host = data->req.cookiehost ? - data->req.cookiehost : conn->host.name; - const bool secure_context = Curl_secure_context(conn, host); + data->req.cookiehost : data->state.origin->hostname; + const bool secure_context = Curl_secure_context(data, host); CURLcode result; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, @@ -3575,8 +3559,8 @@ static CURLcode http_header_s(struct Curl_easy *data, ) ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; if(v) { - CURLcode result = - Curl_hsts_parse(data->hsts, conn->host.name, v); + CURLcode result = Curl_hsts_parse( + data->hsts, data->state.origin->hostname, v); if(result) { if(result == CURLE_OUT_OF_MEMORY) return result; @@ -3827,8 +3811,8 @@ static CURLcode http_size(struct Curl_easy *data) return CURLE_OK; } -static CURLcode verify_header(struct Curl_easy *data, - const char *hd, size_t hdlen) +CURLcode Curl_verify_header(struct Curl_easy *data, + const char *hd, size_t hdlen) { struct SingleRequest *k = &data->req; const char *ptr = memchr(hd, 0x00, hdlen); @@ -3837,6 +3821,14 @@ static CURLcode verify_header(struct Curl_easy *data, failf(data, "Nul byte in header"); return CURLE_WEIRD_SERVER_REPLY; } + if(hdlen > 2) { + ptr = memchr(hd, '\r', hdlen - 2); + if(ptr) { + /* CR may only precede the LF, nothing else */ + failf(data, "Carriage return found in header"); + return CURLE_WEIRD_SERVER_REPLY; + } + } if(k->headerline < 2) /* the first "header" is the status-line and it has no colon */ return CURLE_OK; @@ -4191,6 +4183,17 @@ static CURLcode http_on_response(struct Curl_easy *data, goto out; } + /* final response without error, prepare to receive the body */ + result = http_firstwrite(data); + if(result) + goto out; + + /* 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); + if(result) + goto out; + /* If we requested a "no body", this is a good time to get * out and return home. */ @@ -4204,14 +4207,6 @@ static CURLcode http_on_response(struct Curl_easy *data, if((k->maxdownload == 0) && (k->httpversion_sent < 20)) k->download_done = TRUE; - /* final response without error, prepare to receive the body */ - result = http_firstwrite(data); - - if(!result) - /* This is the last response that we get for the current request. Check on - * the body size and determine if the response is complete. */ - result = http_size(data); - out: if(last_hd) /* if not written yet, write it now */ @@ -4367,7 +4362,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data, } } - result = verify_header(data, hd, hdlen); + result = Curl_verify_header(data, hd, hdlen); if(result) return result; @@ -4876,7 +4871,6 @@ struct name_const { size_t namelen; }; -/* keep them sorted by length! */ static const struct name_const H2_NON_FIELD[] = { { STRCONST("Host") }, { STRCONST("Upgrade") }, @@ -4890,10 +4884,8 @@ static bool h2_permissible_field(struct dynhds_entry *e) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(H2_NON_FIELD); ++i) { - if(e->namelen < H2_NON_FIELD[i].namelen) - return TRUE; if(e->namelen == H2_NON_FIELD[i].namelen && - curl_strequal(H2_NON_FIELD[i].name, e->name)) + curl_strnequal(H2_NON_FIELD[i].name, e->name, e->namelen)) return FALSE; } return TRUE; @@ -4950,7 +4942,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_xfer_is_secure(data) ? "https" : "http"; + scheme = data->state.origin->scheme->name; } } diff --git a/lib/http.h b/lib/http.h index 6e33c00e9219..5050215743f2 100644 --- a/lib/http.h +++ b/lib/http.h @@ -83,8 +83,6 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, CURLcode Curl_add_timecondition(struct Curl_easy *data, struct dynbuf *req); CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect, int httpversion, struct dynbuf *req); -CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect, - struct dynhds *hds); void Curl_http_to_fold(struct dynbuf *bf); @@ -108,6 +106,11 @@ CURLcode Curl_http_write_resp_hd(struct Curl_easy *data, const char *hd, size_t hdlen, bool is_eos); +/* check a received header line for forbidden bytes/format, the same checks + applied to regular response headers */ +CURLcode Curl_verify_header(struct Curl_easy *data, + const char *hd, size_t hdlen); + /* These functions are in http.c */ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, const char *auth); @@ -180,8 +183,9 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, * @param request pointer to the request keyword * @param httpreq is the request type * @param path pointer to the requested path - * @param proxytunnel boolean if this is the request setting up a "proxy - * tunnel" + * @param query pointer to the requested query or NULL + * @param is_connect boolean if this is a CONNECT request + * (where httpreq is HTTPREQ_GET since there is no HTTPREQ_CONNECT) * * @returns CURLcode */ @@ -190,9 +194,8 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, const char *request, Curl_HttpReq httpreq, const char *path, - bool proxytunnel); /* TRUE if this is - the request setting up - the proxy tunnel */ + const char *query, + bool is_connect); /* Decode HTTP status code string. */ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len); diff --git a/lib/http2.c b/lib/http2.c index 7be5abdd3147..a820439ef45a 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -47,14 +47,11 @@ #include "curlx/dynbuf.h" #include "headers.h" -#if (NGHTTP2_VERSION_NUM < 0x010c00) -#error too old nghttp2 version, upgrade! +#if NGHTTP2_VERSION_NUM < 0x010f00 +#error "nghttp2 1.15.0 or greater required" #endif -#if (NGHTTP2_VERSION_NUM >= 0x010c00) #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1 -#endif - /* buffer dimensioning: * use 16K as chunk size, as that fits H2 DATA frames well */ @@ -189,6 +186,8 @@ static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade) static void cf_h2_ctx_free(struct cf_h2_ctx *ctx) { if(ctx && ctx->initialized) { + if(ctx->h2) + nghttp2_session_del(ctx->h2); Curl_bufq_free(&ctx->inbufq); Curl_bufq_free(&ctx->outbufq); Curl_bufcp_free(&ctx->stream_bufcp); @@ -199,14 +198,6 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *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 @@ -221,8 +212,8 @@ static uint32_t cf_h2_initial_win_size(struct Curl_easy *data) } static size_t populate_settings(nghttp2_settings_entry *iv, - struct Curl_easy *data, - struct cf_h2_ctx *ctx) + 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); @@ -232,7 +223,7 @@ static size_t populate_settings(nghttp2_settings_entry *iv, 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; + iv[2].value = !!data->multi->push_cb; return 3; } @@ -469,8 +460,7 @@ static int h2_client_new(struct Curl_cfilter *cf, return rc; /* We handle window updates ourself to enforce buffer limits */ nghttp2_option_set_no_auto_window_update(o, 1); -#if NGHTTP2_VERSION_NUM >= 0x013200 - /* with 1.50.0 */ +#if NGHTTP2_VERSION_NUM >= 0x013200 /* with 1.50.0 */ /* turn off RFC 9113 leading and trailing white spaces validation against HTTP field value. */ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); @@ -859,7 +849,7 @@ static int push_promise(struct Curl_cfilter *cf, result = http2_data_setup(cf, newhandle, &newstream); if(result) { - failf(data, "error setting up stream: %d", result); + failf(data, "error setting up stream: %d", (int)result); discard_newhandle(cf, newhandle); rv = CURL_PUSH_DENY; goto fail; @@ -908,7 +898,7 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf, stream->xfer_result = cf_h2_update_local_win(cf, data, stream); if(stream->xfer_result) CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers", - stream->id, stream->xfer_result, blen); + stream->id, (int)stream->xfer_result, blen); } } @@ -925,7 +915,7 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf, struct cf_h2_ctx *ctx = cf->ctx; CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, " "RST-ing stream", - stream->id, stream->xfer_result, blen); + stream->id, (int)stream->xfer_result, blen); nghttp2_submit_rst_stream(ctx->h2, 0, stream->id, (uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE); } @@ -1389,7 +1379,7 @@ static void cf_h2_header_error(struct Curl_cfilter *cf, { struct cf_h2_ctx *ctx = cf->ctx; - failf(data, "Error receiving HTTP2 header: %d(%s)", result, + failf(data, "Error receiving HTTP2 header: %d(%s)", (int)result, curl_easy_strerror(result)); if(stream) { nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1409,7 +1399,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, struct Curl_cfilter *cf = userp; struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream; - struct Curl_easy *data_s; + struct Curl_easy *data; int32_t stream_id = frame->hd.stream_id; CURLcode result; (void)flags; @@ -1417,15 +1407,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ /* get the stream from the hash based on Stream ID */ - data_s = nghttp2_session_get_stream_user_data(session, stream_id); - if(!GOOD_EASY_HANDLE(data_s)) + data = nghttp2_session_get_stream_user_data(session, stream_id); + if(!GOOD_EASY_HANDLE(data)) /* Receiving a Stream ID not in the hash should not happen, this is an internal error more than anything else! */ return NGHTTP2_ERR_CALLBACK_FAILURE; - stream = H2_STREAM_CTX(ctx, data_s); + stream = H2_STREAM_CTX(ctx, data); if(!stream) { - failf(data_s, "Internal NULL stream"); + failf(data, "Internal NULL stream"); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1438,14 +1428,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, !strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) { /* pseudo headers are lower case */ int rc = 0; - char *check = curl_maprintf("%s:%d", cf->conn->host.name, - cf->conn->remote_port); + char *check = curl_maprintf("%s:%d", data->state.origin->hostname, + data->state.origin->port); if(!check) /* no memory */ return NGHTTP2_ERR_CALLBACK_FAILURE; if(!curl_strequal(check, (const char *)value) && - ((cf->conn->remote_port != cf->conn->given->defport) || - !curl_strequal(cf->conn->host.name, (const char *)value))) { + ((data->state.origin->port != cf->conn->given->defport) || + !curl_strequal(data->state.origin->hostname, (const char *)value))) { /* This is push is not for the same authority that was asked for in * the URL. RFC 7540 section 8.2 says: "A client MUST treat a * PUSH_PROMISE for which the server is not authoritative as a stream @@ -1473,7 +1463,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, char **headp; if(stream->push_headers_alloc > 1000) { /* this is beyond crazy many headers, bail out */ - failf(data_s, "Too many PUSH_PROMISE headers"); + failf(data, "Too many PUSH_PROMISE headers"); free_push_headers(stream); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1497,13 +1487,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(stream->bodystarted) { /* This is a trailer */ - CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s", stream->id, (int)namelen, name, (int)valuelen, value); result = Curl_dynhds_add(&stream->resp_trailers, (const char *)name, namelen, (const char *)value, valuelen); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1511,21 +1501,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, } if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 && - memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) { + !memcmp(HTTP_PSEUDO_STATUS, name, namelen)) { /* 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); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r", stream->status_code); - result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO); + result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } curlx_dyn_reset(&ctx->scratch); @@ -1535,17 +1525,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(!result) result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); if(!result) - h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch), + h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), curlx_dyn_len(&ctx->scratch), FALSE); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } /* if we receive data for another handle, wake that up */ - if(CF_DATA_CURRENT(cf) != data_s) - Curl_multi_mark_dirty(data_s); + if(CF_DATA_CURRENT(cf) != data) + Curl_multi_mark_dirty(data); - CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d", + CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", stream->id, stream->status_code); return 0; } @@ -1562,17 +1552,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(!result) result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n")); if(!result) - h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch), + h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), curlx_dyn_len(&ctx->scratch), FALSE); if(result) { - cf_h2_header_error(cf, data_s, stream, result); + cf_h2_header_error(cf, data, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } /* if we receive data for another handle, wake that up */ - if(CF_DATA_CURRENT(cf) != data_s) - Curl_multi_mark_dirty(data_s); + if(CF_DATA_CURRENT(cf) != data) + Curl_multi_mark_dirty(data); - CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s", + CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s", stream->id, (int)namelen, name, (int)valuelen, value); return 0; /* 0 is successful */ @@ -1620,7 +1610,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session, nread = (ssize_t)n; CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d", - stream_id, length, stream->body_eos, nread, result); + stream_id, length, stream->body_eos, nread, (int)result); if(stream->body_eos && Curl_bufq_is_empty(&stream->sendbuf)) { *data_flags = NGHTTP2_DATA_FLAG_EOF; @@ -1701,12 +1691,12 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } else if(stream->resp_hds_complete && data->req.no_body) { - CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did " - "not want a body anyway, ignore: %s (err %u)", - stream->id, nghttp2_http2_strerror(stream->error), - stream->error); - stream->close_handled = TRUE; - return CURLE_OK; + 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 %d reset by %s (error 0x%x %s)", stream->id, stream->reset_by_server ? "server" : "curl", @@ -1754,7 +1744,7 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, result = CURLE_OK; out: - CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", result, *pnlen); + CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", (int)result, *pnlen); return result; } @@ -1778,16 +1768,12 @@ static int sweight_in_effect(const struct Curl_easy *data) * struct. */ -static void h2_pri_spec(struct cf_h2_ctx *ctx, - struct Curl_easy *data, +static void h2_pri_spec(struct Curl_easy *data, nghttp2_priority_spec *pri_spec) { struct Curl_data_priority *prio = &data->set.priority; - struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent); - int32_t depstream_id = depstream ? depstream->id : 0; - nghttp2_priority_spec_init(pri_spec, depstream_id, - sweight_wanted(data), - data->set.priority.exclusive); + nghttp2_priority_spec_init(pri_spec, 0, + sweight_wanted(data), FALSE); data->state.priority = *prio; } @@ -1805,13 +1791,11 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, int rv = 0; 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))) { + (sweight_wanted(data) != sweight_in_effect(data))) { /* send new weight and/or dependency */ nghttp2_priority_spec pri_spec; - h2_pri_spec(ctx, data, &pri_spec); + h2_pri_spec(data, &pri_spec); CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id); DEBUGASSERT(stream->id != -1); rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE, @@ -1869,7 +1853,7 @@ static CURLcode stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(result && (result != CURLE_AGAIN)) CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %d, %zu", - stream->id, len, result, *pnread); + stream->id, len, (int)result, *pnread); return result; } @@ -1920,7 +1904,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); if(result) { if(result != CURLE_AGAIN) { - failf(data, "Failed receiving HTTP2 data: %d(%s)", result, + failf(data, "Failed receiving HTTP2 data: %d(%s)", (int)result, curl_easy_strerror(result)); return result; } @@ -2015,7 +1999,7 @@ static CURLcode cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu, " "window=%d/%d, connection %d/%d", - stream->id, len, result, *pnread, + stream->id, len, (int)result, *pnread, nghttp2_session_get_stream_effective_recv_data_length( ctx->h2, stream->id), nghttp2_session_get_stream_effective_local_window_size( @@ -2123,7 +2107,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream, goto out; } - h2_pri_spec(ctx, data, &pri_spec); + h2_pri_spec(data, &pri_spec); if(!nghttp2_session_check_request_allowed(ctx->h2)) CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)"); @@ -2200,7 +2184,7 @@ 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); + stream ? stream->id : -1, (int)result, *pnwritten); curlx_safefree(nva); *pstream = stream; Curl_dynhds_free(&h2_headers); @@ -2234,7 +2218,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(eos); 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); + stream->id, (int)result, n, eos); if(result) goto out; *pnwritten = len; @@ -2242,7 +2226,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, else { 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); + stream->id, len, (int)result, *pnwritten, eos); } /* Call the nghttp2 send loop and flush to write ALL buffered data, @@ -2278,7 +2262,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, " "eos=%d, h2 windows %d-%d (stream-conn), " "buffers %zu-%zu (stream-conn)", - stream->id, len, result, *pnwritten, + stream->id, len, (int)result, *pnwritten, stream->body_eos, nghttp2_session_get_stream_remote_window_size( ctx->h2, stream->id), @@ -2289,7 +2273,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, else { CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zu, " "connection-window=%d, nw_send_buffer(%zu)", - len, result, *pnwritten, + len, (int)result, *pnwritten, nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->outbufq)); } @@ -2322,7 +2306,7 @@ static CURLcode cf_h2_flush(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "[%d] flush -> %d, " "h2 windows %d-%d (stream-conn), " "buffers %zu-%zu (stream-conn)", - stream->id, result, + stream->id, (int)result, nghttp2_session_get_stream_remote_window_size( ctx->h2, stream->id), nghttp2_session_get_remote_window_size(ctx->h2), @@ -2332,7 +2316,7 @@ static CURLcode cf_h2_flush(struct Curl_cfilter *cf, else { CURL_TRC_CF(data, cf, "flush -> %d, " "connection-window=%d, nw_send_buffer(%zu)", - result, nghttp2_session_get_remote_window_size(ctx->h2), + (int)result, nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->outbufq)); } CF_DATA_RESTORE(cf, save); @@ -2551,27 +2535,11 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, result = CURLE_OK; out: - CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done); + CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", (int)result, *done); CF_DATA_RESTORE(cf, save); return result; } -static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_h2_ctx *ctx = cf->ctx; - - if(ctx) { - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_h2_ctx_close(ctx); - CF_DATA_RESTORE(cf, save); - cf->connected = FALSE; - } - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; @@ -2794,7 +2762,6 @@ struct Curl_cftype Curl_cft_nghttp2 = { CURL_LOG_LVL_NONE, cf_h2_destroy, cf_h2_connect, - cf_h2_close, cf_h2_shutdown, cf_h2_adjust_pollset, cf_h2_data_pending, @@ -2869,9 +2836,7 @@ bool Curl_http2_may_switch(struct Curl_easy *data) (data->state.http_neg.wanted & CURL_HTTP_V2x) && data->state.http_neg.h2_prior_knowledge) { #ifndef CURL_DISABLE_PROXY - if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) { - /* We do not support HTTP/2 proxies yet. Also it is debatable - whether or not this setting should apply to HTTP/2 proxies. */ + if(data->conn->bits.origin_is_proxy) { infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); return FALSE; } @@ -2951,7 +2916,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, result = Curl_bufq_write(&ctx->inbufq, (const unsigned char *)mem, nread, &copied); if(result) { - failf(data, "error on copying HTTP Upgrade response: %d", result); + failf(data, "error on copying HTTP Upgrade response: %d", (int)result); return CURLE_RECV_ERROR; } if(copied < nread) { diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 55efa120478f..780eed67b931 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -271,13 +271,12 @@ static bool should_urlencode(struct Curl_str *service_name) * 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 FALSE; } - return true; + return TRUE; } /* maximum length for the aws sivg4 parts */ @@ -384,7 +383,7 @@ static CURLcode make_headers(struct Curl_easy *data, char date_full_hdr[DATE_FULL_HDR_LEN]; struct curl_slist *head = NULL; struct curl_slist *tmp_head = NULL; - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; struct curl_slist *l; bool again = TRUE; @@ -516,8 +515,8 @@ static CURLcode make_headers(struct Curl_easy *data, } } while(again); - ret = merge_duplicate_headers(head); - if(ret) + result = merge_duplicate_headers(head); + if(result) goto fail; for(l = head; l; l = l->next) { @@ -540,11 +539,11 @@ static CURLcode make_headers(struct Curl_easy *data, goto fail; } - ret = CURLE_OK; + result = CURLE_OK; fail: curl_slist_free_all(head); - return ret; + return result; } #define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256")) @@ -618,12 +617,12 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, bool empty_payload = (empty_method || data->set.filesize == 0); /* The POST payload is in memory */ bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields); - CURLcode ret = CURLE_OUT_OF_MEMORY; + CURLcode result = CURLE_OUT_OF_MEMORY; if(empty_payload || post_payload) { /* Calculate a real hash when we know the request payload */ - ret = calc_payload_hash(data, sha_hash, sha_hex); - if(ret) + result = calc_payload_hash(data, sha_hash, sha_hex); + if(result) goto fail; } else { @@ -638,9 +637,9 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data, curl_msnprintf(header, CONTENT_SHA256_HDR_LEN, "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex); - ret = CURLE_OK; + result = CURLE_OK; fail: - return ret; + return result; } static int compare_func(const void *a, const void *b) @@ -818,66 +817,14 @@ UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) return result; } -CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) +static CURLcode parse_sigv4_params(struct Curl_easy *data, + const char *hostname, + struct Curl_str *provider0, + struct Curl_str *provider1, + struct Curl_str *region, + struct Curl_str *service) { - CURLcode result = CURLE_OUT_OF_MEMORY; - struct connectdata *conn = data->conn; - const char *line; - struct Curl_str provider0; - struct Curl_str provider1; - struct Curl_str region = { NULL, 0 }; - struct Curl_str service = { NULL, 0 }; - const char *hostname = conn->host.name; - time_t clock; - struct tm tm; - char timestamp[TIMESTAMP_SIZE]; - char date[9]; - struct dynbuf canonical_headers; - struct dynbuf signed_headers; - struct dynbuf canonical_query; - struct dynbuf canonical_path; - char *date_header = NULL; - Curl_HttpReq httpreq; - const char *method = NULL; - const char *payload_hash = NULL; - size_t payload_hash_len = 0; - unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; - char sha_hex[SHA256_HEX_LENGTH]; - char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ - char *canonical_request = NULL; - char *request_type = NULL; - char *credential_scope = NULL; - char *str_to_sign = NULL; - const char *user = data->state.aptr.user ? data->state.aptr.user : ""; - char *secret = NULL; - unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; - unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; - char *auth_headers = NULL; - - if(data->set.path_as_is) { - failf(data, "Cannot use sigv4 authentication with path-as-is flag"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - if(Curl_checkheaders(data, STRCONST("Authorization"))) { - /* Authorization already present, Bailing out */ - return CURLE_OK; - } - - /* we init those buffers here, so goto fail will free initialized dynbuf */ - curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); - curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); - - /* - * Parameters parsing - * Google and Outscale use the same OSC or GOOG, - * but Amazon uses AWS and AMZ for header arguments. - * AWS is the default because most of non-amazon providers - * are still using aws:amz as a prefix. - */ - line = data->set.str[STRING_AWS_SIGV4]; + const char *line = data->set.str[STRING_AWS_SIGV4]; if(!line || !*line) line = "aws:amz"; @@ -885,71 +832,89 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) No string can be longer than N bytes of non-whitespace */ - if(curlx_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) { + if(curlx_str_until(&line, provider0, MAX_SIGV4_LEN, ':')) { failf(data, "first aws-sigv4 provider cannot be empty"); - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto fail; + return CURLE_BAD_FUNCTION_ARGUMENT; } if(curlx_str_single(&line, ':') || - curlx_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) { - provider1 = provider0; + curlx_str_until(&line, provider1, MAX_SIGV4_LEN, ':')) { + *provider1 = *provider0; } else if(curlx_str_single(&line, ':') || - curlx_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') || + curlx_str_until(&line, region, MAX_SIGV4_LEN, ':') || curlx_str_single(&line, ':') || - curlx_str_until(&line, &service, MAX_SIGV4_LEN, ':')) { + curlx_str_until(&line, service, MAX_SIGV4_LEN, ':')) { /* nothing to do */ } - if(!curlx_strlen(&service)) { + if(!curlx_strlen(service)) { const char *p = hostname; - if(curlx_str_until(&p, &service, MAX_SIGV4_LEN, '.') || + if(curlx_str_until(&p, service, MAX_SIGV4_LEN, '.') || curlx_str_single(&p, '.')) { failf(data, "aws-sigv4: service missing in parameters and hostname"); - result = CURLE_URL_MALFORMAT; - goto fail; + return CURLE_URL_MALFORMAT; } infof(data, "aws_sigv4: picked service %.*s from host", - (int)curlx_strlen(&service), curlx_str(&service)); + (int)curlx_strlen(service), curlx_str(service)); - if(!curlx_strlen(®ion)) { - if(curlx_str_until(&p, ®ion, MAX_SIGV4_LEN, '.') || + if(!curlx_strlen(region)) { + if(curlx_str_until(&p, region, MAX_SIGV4_LEN, '.') || curlx_str_single(&p, '.')) { failf(data, "aws-sigv4: region missing in parameters and hostname"); - result = CURLE_URL_MALFORMAT; - goto fail; + return CURLE_URL_MALFORMAT; } infof(data, "aws_sigv4: picked region %.*s from host", - (int)curlx_strlen(®ion), curlx_str(®ion)); + (int)curlx_strlen(region), curlx_str(region)); } } - Curl_http_method(data, &method, &httpreq); + return CURLE_OK; +} - payload_hash = - parse_content_sha_hdr(data, curlx_str(&provider1), - curlx_strlen(&provider1), &payload_hash_len); +static CURLcode get_payload_hash(struct Curl_easy *data, + Curl_HttpReq httpreq, + struct Curl_str *provider0, + struct Curl_str *provider1, + struct Curl_str *service, + unsigned char *sha_hash, + char *sha_hex, + char *content_sha256_hdr, + const char **payload_hash_out, + size_t *payload_hash_len_out) +{ + *payload_hash_out = + parse_content_sha_hdr(data, curlx_str(provider1), + curlx_strlen(provider1), payload_hash_len_out); - if(!payload_hash) { + if(!*payload_hash_out) { + CURLcode result; /* 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"); + bool sign_as_s3 = curlx_str_casecompare(provider0, "aws") && + curlx_str_casecompare(service, "s3"); if(sign_as_s3) - result = calc_s3_payload_hash(data, httpreq, curlx_str(&provider1), - curlx_strlen(&provider1), sha_hash, + result = calc_s3_payload_hash(data, httpreq, curlx_str(provider1), + curlx_strlen(provider1), sha_hash, sha_hex, content_sha256_hdr); else result = calc_payload_hash(data, sha_hash, sha_hex); if(result) - goto fail; + return result; - payload_hash = sha_hex; + *payload_hash_out = sha_hex; /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */ - payload_hash_len = strlen(sha_hex); + *payload_hash_len_out = strlen(sha_hex); } + return CURLE_OK; +} + +static CURLcode get_timestamp(char *timestamp, size_t stampsize) +{ + time_t clock; + struct tm tm; + CURLcode result; #ifdef DEBUGBUILD { @@ -963,43 +928,54 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) clock = time(NULL); #endif result = curlx_gmtime(clock, &tm); - if(result) { - goto fail; - } - if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { - result = CURLE_OUT_OF_MEMORY; - goto fail; - } + if(result) + return result; + + if(!strftime(timestamp, stampsize, "%Y%m%dT%H%M%SZ", &tm)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static CURLcode make_canonical_request(struct Curl_easy *data, + const char *hostname, + char *timestamp, + struct Curl_str *provider1, + struct Curl_str *service, + const char *method, + const char *payload_hash, + size_t payload_hash_len, + char **date_header_out, + char *content_sha256_hdr, + struct dynbuf *canonical_headers, + struct dynbuf *signed_headers, + char **canonical_request_out) +{ + struct dynbuf canonical_query; + struct dynbuf canonical_path; + CURLcode result; + + curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER); result = make_headers(data, hostname, timestamp, - curlx_str(&provider1), curlx_strlen(&provider1), - &date_header, content_sha256_hdr, - &canonical_headers, &signed_headers); + curlx_str(provider1), curlx_strlen(provider1), + date_header_out, content_sha256_hdr, + canonical_headers, signed_headers); if(result) goto fail; - if(*content_sha256_hdr) { - /* make_headers() needed this without the \r\n for canonicalization */ - size_t hdrlen = strlen(content_sha256_hdr); - DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr)); - memcpy(content_sha256_hdr + hdrlen, "\r\n", 3); - } - - memcpy(date, timestamp, sizeof(date)); - date[sizeof(date) - 1] = 0; - result = canon_query(data->state.up.query, &canonical_query); if(result) goto fail; result = canon_path(data->state.up.path, strlen(data->state.up.path), &canonical_path, - should_urlencode(&service)); + should_urlencode(service)); if(result) goto fail; - result = CURLE_OUT_OF_MEMORY; - canonical_request = + *canonical_request_out = curl_maprintf("%s\n" /* HTTPRequestMethod */ "%s\n" /* CanonicalURI */ "%s\n" /* CanonicalQueryString */ @@ -1010,37 +986,65 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) 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), + curlx_dyn_ptr(canonical_headers), + curlx_dyn_ptr(signed_headers), (int)payload_hash_len, payload_hash); - if(!canonical_request) + if(!*canonical_request_out) { + result = CURLE_OUT_OF_MEMORY; goto fail; + } + + result = CURLE_OK; +fail: + curlx_dyn_free(&canonical_query); + curlx_dyn_free(&canonical_path); + return result; +} - infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]", - canonical_request); +static CURLcode make_string_to_sign(struct Curl_easy *data, + struct Curl_str *provider0, + struct Curl_str *region, + struct Curl_str *service, + const char *date, + const char *timestamp, + const char *canonical_request, + char **request_type_out, + char **credential_scope_out, + char **str_to_sign_out) +{ + char *request_type; + char *credential_scope; + char *str_to_sign; + unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; + char sha_hex[SHA256_HEX_LENGTH]; request_type = curl_maprintf("%.*s4_request", - (int)curlx_strlen(&provider0), - curlx_str(&provider0)); + (int)curlx_strlen(provider0), + curlx_str(provider0)); if(!request_type) - goto fail; + return CURLE_OUT_OF_MEMORY; /* provider0 is lowercased *after* curl_maprintf() so that the buffer can be written to */ - Curl_strntolower(request_type, request_type, curlx_strlen(&provider0)); + Curl_strntolower(request_type, request_type, curlx_strlen(provider0)); credential_scope = curl_maprintf("%s/%.*s/%.*s/%s", date, - (int)curlx_strlen(®ion), - curlx_str(®ion), - (int)curlx_strlen(&service), - curlx_str(&service), + (int)curlx_strlen(region), + curlx_str(region), + (int)curlx_strlen(service), + curlx_str(service), request_type); - if(!credential_scope) - goto fail; + if(!credential_scope) { + curlx_free(request_type); + return CURLE_OUT_OF_MEMORY; + } - if(Curl_sha256it(sha_hash, (unsigned char *)canonical_request, - strlen(canonical_request))) - goto fail; + if(Curl_sha256it(sha_hash, (const unsigned char *)canonical_request, + strlen(canonical_request))) { + curlx_free(request_type); + curlx_free(credential_scope); + return CURLE_OUT_OF_MEMORY; + } sha256_to_hex(sha_hex, sha_hash); @@ -1052,36 +1056,69 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) "%s\n" /* RequestDateTime */ "%s\n" /* CredentialScope */ "%s", /* HashedCanonicalRequest in hex */ - (int)curlx_strlen(&provider0), - curlx_str(&provider0), + (int)curlx_strlen(provider0), + curlx_str(provider0), timestamp, credential_scope, sha_hex); - if(!str_to_sign) - goto fail; + if(!str_to_sign) { + curlx_free(request_type); + curlx_free(credential_scope); + return CURLE_OUT_OF_MEMORY; + } /* make provider0 part done uppercase */ - Curl_strntoupper(str_to_sign, curlx_str(&provider0), - curlx_strlen(&provider0)); + Curl_strntoupper(str_to_sign, curlx_str(provider0), + curlx_strlen(provider0)); infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]", str_to_sign); - secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(&provider0), - curlx_str(&provider0), data->state.aptr.passwd ? - data->state.aptr.passwd : ""); + *request_type_out = request_type; + *credential_scope_out = credential_scope; + *str_to_sign_out = str_to_sign; + return CURLE_OK; +} + +static CURLcode sign_and_set_auth_headers(struct Curl_easy *data, + struct Curl_str *provider0, + struct Curl_str *region, + struct Curl_str *service, + const char *request_type, + const char *credential_scope, + const char *date, + const char *str_to_sign, + const char *date_header, + const char *content_sha256_hdr, + struct dynbuf *signed_headers) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + const char *passwd = Curl_creds_passwd(data->state.creds); + char *secret = NULL; + unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 }; + unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 }; + char sha_hex[SHA256_HEX_LENGTH]; + char *auth_headers = NULL; + char *user = curl_escape(Curl_creds_user(data->state.creds), 0); + if(!user) + return CURLE_OUT_OF_MEMORY; + + secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(provider0), + curlx_str(provider0), passwd); if(!secret) goto fail; /* make provider0 part done uppercase */ - Curl_strntoupper(secret, curlx_str(&provider0), curlx_strlen(&provider0)); + Curl_strntoupper(secret, curlx_str(provider0), curlx_strlen(provider0)); HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0); HMAC_SHA256(sign0, sizeof(sign0), - curlx_str(®ion), curlx_strlen(®ion), sign1); + curlx_str(region), curlx_strlen(region), sign1); HMAC_SHA256(sign1, sizeof(sign1), - curlx_str(&service), curlx_strlen(&service), sign0); - HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); - HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); + curlx_str(service), curlx_strlen(service), sign0); + HMAC_SHA256(sign0, sizeof(sign0), + request_type, strlen(request_type), sign1); + HMAC_SHA256(sign1, sizeof(sign1), + str_to_sign, strlen(str_to_sign), sign0); sha256_to_hex(sha_hex, sign0); @@ -1091,43 +1128,119 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) "Credential=%s/%s, " "SignedHeaders=%s, " "Signature=%s\r\n" + "%s" + "%s%s", + (int)curlx_strlen(provider0), + curlx_str(provider0), + user, + credential_scope, + curlx_dyn_ptr(signed_headers), + sha_hex, /* * 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) { + content_sha256_hdr, + content_sha256_hdr[0] ? "\r\n": ""); + if(!auth_headers) goto fail; - } + /* provider 0 uppercase */ Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], - curlx_str(&provider0), curlx_strlen(&provider0)); + curlx_str(provider0), curlx_strlen(provider0)); - curlx_free(data->req.userpwd); - data->req.userpwd = auth_headers; + curlx_free(data->req.hd_auth); + data->req.hd_auth = auth_headers; data->state.authhost.done = TRUE; result = CURLE_OK; fail: - curlx_dyn_free(&canonical_query); - curlx_dyn_free(&canonical_path); + curlx_free(user); + curlx_free(secret); + return result; +} + +CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) +{ + CURLcode result = CURLE_OUT_OF_MEMORY; + struct Curl_str provider0 = { NULL, 0 }; + struct Curl_str provider1 = { NULL, 0 }; + struct Curl_str region = { NULL, 0 }; + struct Curl_str service = { NULL, 0 }; + const char *hostname = data->state.origin->hostname; + char timestamp[TIMESTAMP_SIZE]; + char date[9]; + struct dynbuf canonical_headers; + struct dynbuf signed_headers; + char *date_header = NULL; + Curl_HttpReq httpreq; + const char *method = NULL; + const char *payload_hash = NULL; + size_t payload_hash_len = 0; + unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH]; + char sha_hex[SHA256_HEX_LENGTH]; + char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ + char *canonical_request = NULL; + char *request_type = NULL; + char *credential_scope = NULL; + char *str_to_sign = NULL; + + if(data->set.path_as_is) { + failf(data, "Cannot use sigv4 authentication with path-as-is flag"); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + + if(Curl_checkheaders(data, STRCONST("Authorization"))) + /* Authorization already present, Bailing out */ + return CURLE_OK; + + /* we init those buffers here, so goto fail will free initialized dynbuf */ + curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); + curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); + + result = parse_sigv4_params(data, hostname, &provider0, &provider1, + ®ion, &service); + if(!result) { + Curl_http_method(data, &method, &httpreq); + result = get_payload_hash(data, httpreq, &provider0, &provider1, &service, + sha_hash, sha_hex, content_sha256_hdr, + &payload_hash, &payload_hash_len); + } + + if(!result) + result = get_timestamp(timestamp, sizeof(timestamp)); + + if(!result) + result = make_canonical_request(data, hostname, timestamp, + &provider1, &service, + method, payload_hash, payload_hash_len, + &date_header, content_sha256_hdr, + &canonical_headers, &signed_headers, + &canonical_request); + if(!result) { + /* the timestamp might have been updated in make_canonical_request */ + memcpy(date, timestamp, sizeof(date) - 1); + date[sizeof(date) - 1] = 0; + + result = make_string_to_sign(data, &provider0, ®ion, &service, + date, timestamp, canonical_request, + &request_type, &credential_scope, + &str_to_sign); + } + if(!result) + result = sign_and_set_auth_headers(data, &provider0, ®ion, &service, + request_type, credential_scope, + date, str_to_sign, date_header, + content_sha256_hdr, &signed_headers); + curlx_dyn_free(&canonical_headers); curlx_dyn_free(&signed_headers); 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; } diff --git a/lib/http_chunks.c b/lib/http_chunks.c index aa45c79e3853..9596fc6693a4 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -27,6 +27,7 @@ #include "urldata.h" /* it includes http_chunks.h */ #include "curl_trc.h" +#include "http.h" /* for Curl_verify_header */ #include "sendf.h" /* for the client write stuff */ #include "curlx/dynbuf.h" #include "multiif.h" @@ -153,7 +154,8 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, if(ch->hexindex == 0) { /* This is illegal data, we received junk where we expected a hexadecimal digit. */ - failf(data, "chunk hex-length char not a hex digit: 0x%x", *buf); + failf(data, "chunk hex-length char not a hex digit: 0x%x", + (unsigned int)*buf); ch->state = CHUNK_FAILED; ch->last_code = CHUNKE_ILLEGAL_HEX; return CURLE_RECV_ERROR; @@ -247,6 +249,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, there was no trailer and we move on */ if(tr) { + size_t trlen; result = curlx_dyn_addn(&ch->trailer, STRCONST("\x0d\x0a")); if(result) { ch->state = CHUNK_FAILED; @@ -254,8 +257,18 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data, return result; } tr = curlx_dyn_ptr(&ch->trailer); + trlen = curlx_dyn_len(&ch->trailer); + + /* a trailer is delivered to the client as a header, so it must pass + the same checks as a regular response header */ + result = Curl_verify_header(data, tr, trlen); + if(result) { + ch->state = CHUNK_FAILED; + ch->last_code = CHUNKE_BAD_CHUNK; + return result; + } + if(!data->set.http_te_skip) { - size_t trlen = curlx_dyn_len(&ch->trailer); if(cw_next) result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_HEADER | @@ -535,7 +548,7 @@ static CURLcode add_last_chunk(struct Curl_easy *data, out: curl_slist_free_all(trailers); CURL_TRC_READ(data, "http_chunk, added last chunk with trailers " - "from client -> %d", result); + "from client -> %d", (int)result); return result; } @@ -583,7 +596,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, (int)result); if(result) return result; } diff --git a/lib/http_digest.c b/lib/http_digest.c index b7007071e77e..25783d096972 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -54,7 +54,7 @@ CURLcode Curl_input_digest(struct Curl_easy *data, } if(!checkprefix("Digest", header) || !ISBLANK(header[6])) - return CURLE_BAD_CONTENT_ENCODING; + return CURLE_AUTH_ERROR; header += strlen("Digest"); curlx_str_passblanks(&header); @@ -62,14 +62,33 @@ CURLcode Curl_input_digest(struct Curl_easy *data, return Curl_auth_decode_digest_http_message(header, digest); } +/* Flush the Digest state if it was created for a different origin or with + different credentials than the ones now in use, then link the current + ones. */ +static void digest_flush_stale(struct digestdata *digest, + struct Curl_peer *peer, + struct Curl_creds *creds) +{ + bool flush = FALSE; + if(digest->origin && !Curl_peer_same_destination(peer, digest->origin)) + flush = TRUE; + else if(digest->creds && !Curl_creds_same(creds, digest->creds)) + flush = TRUE; + + if(flush) + /* flush Digest state */ + Curl_auth_digest_cleanup(digest); + + Curl_peer_link(&digest->origin, peer); + Curl_creds_link(&digest->creds, creds); +} + CURLcode Curl_output_digest(struct Curl_easy *data, bool proxy, const unsigned char *request, const unsigned char *uripath) { CURLcode result; - unsigned char *path = NULL; - const char *tmp = NULL; char *response; size_t len; bool have_chlg; @@ -79,8 +98,7 @@ CURLcode Curl_output_digest(struct Curl_easy *data, char **allocuserpwd; /* Point to the name and password for this */ - const char *userp; - const char *passwdp; + struct Curl_creds *creds = NULL; /* Point to the correct struct with this */ struct digestdata *digest; @@ -91,29 +109,24 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_NOT_BUILT_IN; #else digest = &data->state.proxydigest; - allocuserpwd = &data->req.proxyuserpwd; - userp = data->state.aptr.proxyuser; - passwdp = data->state.aptr.proxypasswd; + digest_flush_stale(digest, data->conn->http_proxy.peer, + data->conn->http_proxy.creds); + allocuserpwd = &data->req.hd_proxy_auth; + creds = data->conn->http_proxy.creds; authp = &data->state.authproxy; #endif } else { + DEBUGASSERT(data->state.origin); digest = &data->state.digest; - allocuserpwd = &data->req.userpwd; - userp = data->state.aptr.user; - passwdp = data->state.aptr.passwd; + digest_flush_stale(digest, data->state.origin, data->state.creds); + allocuserpwd = &data->req.hd_auth; + creds = data->state.creds; authp = &data->state.authhost; } curlx_safefree(*allocuserpwd); - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - #ifdef USE_WINDOWS_SSPI have_chlg = !!digest->input_token; #else @@ -125,36 +138,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data, return CURLE_OK; } - /* 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. - - Apache servers can be set to do the Digest IE-style automatically using - the BrowserMatch feature: - https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#msie - - Further details on Digest implementation differences: - https://web.archive.org/web/2009/fngtps.com/2006/09/http-authentication - */ - - if(authp->iestyle) { - tmp = strchr((const char *)uripath, '?'); - 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 *)curl_maprintf("%.*s", (int)urilen, uripath); - } - } - if(!tmp) - 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); - curlx_free(path); + result = Curl_auth_create_digest_http_message(data, creds, request, + uripath, digest, + &response, &len); if(result) return result; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 74d63d6cc0bb..891369b5bc2b 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -40,8 +40,11 @@ static void http_auth_nego_reset(struct connectdata *conn, { if(proxy) conn->proxy_negotiate_state = GSS_AUTHNONE; - else + else { conn->http_negotiate_state = GSS_AUTHNONE; + Curl_peer_unlink(&conn->creds_origin); + Curl_creds_unlink(&conn->creds); + } if(neg_ctx) Curl_auth_cleanup_spnego(neg_ctx); } @@ -52,10 +55,8 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, CURLcode result; size_t len; - /* Point to the username, password, service and host */ - const char *userp; - const char *passwdp; - const char *service; + /* Point to credentials and host */ + struct Curl_creds *creds = NULL; const char *host; /* Point to the correct struct with this */ @@ -64,22 +65,16 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = conn->http_proxy.user; - passwdp = conn->http_proxy.passwd; - service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; - host = conn->http_proxy.host.name; + creds = conn->http_proxy.creds; + host = conn->http_proxy.peer->hostname; state = conn->proxy_negotiate_state; #else return CURLE_NOT_BUILT_IN; #endif } else { - userp = conn->user; - passwdp = conn->passwd; - service = data->set.str[STRING_SERVICE_NAME] ? - data->set.str[STRING_SERVICE_NAME] : "HTTP"; - host = conn->host.name; + creds = data->state.creds; + host = data->state.origin->hostname; state = conn->http_negotiate_state; } @@ -87,13 +82,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(!neg_ctx) return CURLE_OUT_OF_MEMORY; - /* Not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - /* Obtain the input token, if any */ header += strlen("Negotiate"); curlx_str_passblanks(&header); @@ -135,7 +123,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif /* GSS_C_CHANNEL_BOUND_FLAG */ /* Initialize the security context and decode our challenge */ - result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, + result = Curl_auth_decode_spnego_message(data, creds, "HTTP", host, header, neg_ctx); #ifdef GSS_C_CHANNEL_BOUND_FLAG @@ -145,6 +133,22 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(result) http_auth_nego_reset(conn, neg_ctx, proxy); + if(!result && !proxy) { + /* Start it up. From this time onwards, the connection is tied + * tp the credentials used. */ + if(conn->creds_origin && + !Curl_peer_equal(conn->creds_origin, data->state.origin)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + if(conn->creds && !Curl_creds_same(creds, conn->creds)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + Curl_peer_link(&conn->creds_origin, data->state.origin); + Curl_creds_link(&conn->creds, creds); + } + return result; } @@ -217,13 +221,13 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, if(proxy) { #ifndef CURL_DISABLE_PROXY - curlx_free(data->req.proxyuserpwd); - data->req.proxyuserpwd = userp; + curlx_free(data->req.hd_proxy_auth); + data->req.hd_proxy_auth = userp; #endif } else { - curlx_free(data->req.userpwd); - data->req.userpwd = userp; + curlx_free(data->req.hd_auth); + data->req.hd_auth = userp; } curlx_free(base64); diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 82b050529e00..dc9911fdacb2 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -93,6 +93,8 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, else if(*state == NTLMSTATE_TYPE3) { infof(data, "NTLM handshake rejected"); Curl_auth_ntlm_remove(conn, proxy); + Curl_peer_unlink(&conn->creds_origin); + Curl_creds_unlink(&conn->creds); *state = NTLMSTATE_NONE; return CURLE_REMOTE_ACCESS_DENIED; } @@ -122,10 +124,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) server, which is for a plain host or for an HTTP proxy */ char **allocuserpwd; - /* point to the username, password, service and host */ - const char *userp; - const char *passwdp; - const char *service = NULL; + /* point to credentials and host */ + struct Curl_creds *creds = NULL; const char *hostname = NULL; /* point to the correct struct with this */ @@ -139,12 +139,9 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) if(proxy) { #ifndef CURL_DISABLE_PROXY - 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"; - hostname = conn->http_proxy.host.name; + allocuserpwd = &data->req.hd_proxy_auth; + creds = conn->http_proxy.creds; + hostname = conn->http_proxy.peer->hostname; state = &conn->proxy_ntlm_state; authp = &data->state.authproxy; #else @@ -152,27 +149,18 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) #endif } else { - 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"; - hostname = conn->host.name; + allocuserpwd = &data->req.hd_auth; + creds = data->state.creds; + hostname = data->state.origin->hostname; state = &conn->http_ntlm_state; authp = &data->state.authhost; } + ntlm = Curl_auth_ntlm_get(conn, proxy); if(!ntlm) return CURLE_OUT_OF_MEMORY; authp->done = FALSE; - /* not set means empty */ - if(!userp) - userp = ""; - - if(!passwdp) - passwdp = ""; - #ifdef USE_WINDOWS_SSPI if(!Curl_pSecFn) { /* not thread-safe and leaks - use curl_global_init() to avoid */ @@ -194,9 +182,23 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) switch(*state) { case NTLMSTATE_TYPE1: - default: /* for the weird cases we (re)start here */ - /* Create a type-1 message */ - result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, service, + default: /* for the weird cases we (re)start here */ + if(!proxy) { + /* Start it up. From this time onwards, the connection is tied + * tp the credentials used. */ + if(conn->creds_origin && + !Curl_peer_equal(conn->creds_origin, data->state.origin)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + if(conn->creds && !Curl_creds_same(creds, conn->creds)) { + DEBUGASSERT(0); /* should not happen. */ + return CURLE_FAILED_INIT; + } + Curl_peer_link(&conn->creds_origin, data->state.origin); + Curl_creds_link(&conn->creds, creds); + } + result = Curl_auth_create_ntlm_type1_message(data, creds, "HTTP", hostname, ntlm, &ntlmmsg); if(!result) { DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); @@ -215,8 +217,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) case NTLMSTATE_TYPE2: /* We already received the type-2 message, create a type-3 message */ - result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp, - ntlm, &ntlmmsg); + result = Curl_auth_create_ntlm_type3_message(data, creds, ntlm, &ntlmmsg); if(!result && Curl_bufref_len(&ntlmmsg)) { result = curlx_base64_encode(Curl_bufref_uptr(&ntlmmsg), Curl_bufref_len(&ntlmmsg), &base64, &len); diff --git a/lib/http_proxy.c b/lib/http_proxy.c index a4bdd7e36107..d019796b5324 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -35,11 +35,12 @@ #include "cf-h2-proxy.h" #include "connect.h" #include "vauth/vauth.h" +#include "vquic/vquic.h" #include "curlx/strparse.h" static CURLcode dynhds_add_custom(struct Curl_easy *data, bool is_connect, int httpversion, - struct dynhds *hds) + bool is_udp, struct dynhds *hds) { struct connectdata *conn = data->conn; struct curl_slist *h[2]; @@ -49,11 +50,12 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, enum Curl_proxy_use proxy; - if(is_connect) + if(is_connect && !is_udp) proxy = HEADER_CONNECT; + else if(is_connect && is_udp) + proxy = HEADER_CONNECT_UDP; else - proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ? - HEADER_PROXY : HEADER_SERVER; + proxy = conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER; switch(proxy) { case HEADER_SERVER: @@ -72,6 +74,12 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, else h[0] = data->set.headers; break; + case HEADER_CONNECT_UDP: + if(data->set.sep_headers) + h[0] = data->set.proxyheaders; + else + h[0] = data->set.headers; + break; } /* loop through one or two lists */ @@ -162,52 +170,43 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, return CURLE_OK; } -void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, - const char **phostname, - uint16_t *pport, bool *pipv6_ip) -{ - DEBUGASSERT(cf); - DEBUGASSERT(cf->conn); - - if(cf->conn->bits.conn_to_host) - *phostname = cf->conn->conn_to_host.name; - else if(cf->sockindex == SECONDARYSOCKET) - *phostname = cf->conn->secondaryhostname; - else - *phostname = cf->conn->host.name; - - if(cf->sockindex == SECONDARYSOCKET) - *pport = cf->conn->secondary_port; - else if(cf->conn->bits.conn_to_port) - *pport = cf->conn->conn_to_port; - else - *pport = cf->conn->remote_port; - - *pipv6_ip = (strchr(*phostname, ':') != NULL); -} - struct cf_proxy_ctx { - int httpversion; /* HTTP version used to CONNECT */ + struct Curl_peer *peer; /* proxy */ + struct Curl_peer *tunnel_peer; /* tunnel destination */ + uint8_t proxytype; + uint8_t tunnel_transport; BIT(sub_filter_installed); }; -CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, - struct Curl_cfilter *cf, - struct Curl_easy *data, - int http_version_major) +static int proxy_http_ver_major(proxy_http_ver ver) +{ + switch(ver) { + case PROXY_HTTP_V1: + return 11; + case PROXY_HTTP_V2: + return 20; + case PROXY_HTTP_V3: + return 30; + } + return 0; +} + +static CURLcode http_proxy_create_CONNECT(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_peer *dest, + proxy_http_ver ver) { - struct cf_proxy_ctx *ctx = cf->ctx; - const char *hostname = NULL; char *authority = NULL; - uint16_t port; - bool ipv6_ip; + int httpversion = proxy_http_ver_major(ver); CURLcode result; struct httpreq *req = NULL; - Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - - authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, - ipv6_ip ? "]" : "", port); + authority = curl_maprintf("%s%s%s:%u", + dest->ipv6 ? "[" : "", + dest->hostname, + dest->ipv6 ? "]" : "", + dest->port); if(!authority) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -221,21 +220,21 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, /* Setup the proxy-authorization header, if any */ result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, - req->authority, TRUE); + req->authority, NULL, TRUE); if(result) goto out; /* If user is not overriding Host: header, we add for HTTP/1.x */ - if(http_version_major == 1 && + if(ver == PROXY_HTTP_V1 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { result = Curl_dynhds_cadd(&req->headers, "Host", authority); if(result) goto out; } - if(data->req.proxyuserpwd) { + if(data->req.hd_proxy_auth) { result = Curl_dynhds_h1_cadd_line(&req->headers, - data->req.proxyuserpwd); + data->req.hd_proxy_auth); if(result) goto out; } @@ -248,14 +247,15 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, goto out; } - if(http_version_major == 1 && + if(ver == PROXY_HTTP_V1 && !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); if(result) goto out; } - result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers); + result = dynhds_add_custom(data, TRUE, httpversion, + FALSE, &req->headers); out: if(result && req) { @@ -267,66 +267,385 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, return result; } +static CURLcode http_proxy_create_CONNECTUDP(struct httpreq **preq, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_peer *dest, + proxy_http_ver ver) +{ + const char *proxy_scheme = "http"; + const char *proxy_host = cf->conn->http_proxy.peer->hostname; + int httpversion = proxy_http_ver_major(ver); + char *authority = NULL; + char *path = NULL; + char *encoded_host = NULL; + struct httpreq *req = NULL; + bool proxy_ipv6_ip; + CURLcode result; + + if(cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS || + cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS2 || + cf->conn->http_proxy.proxytype == CURLPROXY_HTTPS3) + proxy_scheme = "https"; + + proxy_ipv6_ip = cf->conn->http_proxy.peer->ipv6 != 0; + + authority = curl_maprintf("%s%s%s:%d", + proxy_ipv6_ip ? "[" : "", + proxy_host, + proxy_ipv6_ip ? "]" : "", + cf->conn->http_proxy.peer->port); + if(!authority) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(dest->ipv6) { + /* RFC 9298: colons in IPv6 addresses MUST be percent-encoded + * in the URI template (e.g. "2001:db8::1" -> "2001%3Adb8%3A%3A1") */ + const char *s = dest->hostname; + char *d; + size_t hlen = strlen(s); + encoded_host = curlx_malloc(hlen * 3 + 1); + if(!encoded_host) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + d = encoded_host; + while(*s) { + if(*s == ':') { + *d++ = '%'; + *d++ = '3'; + *d++ = 'A'; + } + else + *d++ = *s; + s++; + } + *d = '\0'; + path = curl_maprintf("/.well-known/masque/udp/%s/%u/", + encoded_host, (unsigned int)dest->port); + } + else { + path = curl_maprintf("/.well-known/masque/udp/%s/%u/", + dest->hostname, (unsigned int)dest->port); + } + + if(!path) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(ver == PROXY_HTTP_V1) { + result = Curl_http_req_make(&req, "GET", sizeof("GET")-1, + proxy_scheme, strlen(proxy_scheme), + authority, strlen(authority), + path, strlen(path)); + if(result) + goto out; + } + else if(ver == PROXY_HTTP_V2 || ver == PROXY_HTTP_V3) { + result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT") - 1, + proxy_scheme, strlen(proxy_scheme), + authority, strlen(authority), + path, strlen(path)); + if(result) + goto out; + } + else { + result = CURLE_FAILED_INIT; + goto out; + } + + /* Setup the proxy-authorization header, if any */ + result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, + req->authority, NULL, TRUE); + if(result) + goto out; + + /* If user is not overriding Host: header, we add for HTTP/1.x */ + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { + result = Curl_dynhds_cadd(&req->headers, "Host", authority); + if(result) + goto out; + } + + if(data->req.hd_proxy_auth) { + result = Curl_dynhds_h1_cadd_line(&req->headers, + data->req.hd_proxy_auth); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && + data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { + result = Curl_dynhds_cadd(&req->headers, "User-Agent", + data->set.str[STRING_USERAGENT]); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1 && + !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { + result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); + if(result) + goto out; + } + + if(ver == PROXY_HTTP_V1) { + result = Curl_dynhds_cadd(&req->headers, "Connection", "Upgrade"); + if(result) + goto out; + + result = Curl_dynhds_cadd(&req->headers, "Upgrade", "connect-udp"); + if(result) + goto out; + + result = Curl_dynhds_cadd(&req->headers, "Capsule-Protocol", "?1"); + if(result) + goto out; + } + else { + result = Curl_dynhds_cadd(&req->headers, ":Protocol", "connect-udp"); + if(result) + goto out; + + if(ver >= PROXY_HTTP_V2) { + result = Curl_dynhds_cadd(&req->headers, "Capsule-Protocol", "?1"); + if(result) + goto out; + } + } + + result = dynhds_add_custom(data, TRUE, httpversion, + TRUE, &req->headers); + +out: + if(result && req) { + Curl_http_req_free(req); + req = NULL; + } + curlx_free(authority); + curlx_free(path); + curlx_free(encoded_host); + *preq = req; + return result; +} + +CURLcode Curl_http_proxy_create_tunnel_request( + struct httpreq **preq, struct Curl_cfilter *cf, + struct Curl_easy *data, struct Curl_peer *dest, + proxy_http_ver ver, bool udp_tunnel) +{ + CURLcode result; + + if(udp_tunnel) + result = http_proxy_create_CONNECTUDP(preq, cf, data, dest, ver); + else + result = http_proxy_create_CONNECT(preq, cf, data, dest, ver); + if(result) + return result; + + if(udp_tunnel) + infof(data, "Establishing %s proxy UDP tunnel to %s:%s", + (ver == PROXY_HTTP_V2) ? "HTTP/2" : + (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP", + data->state.up.hostname, data->state.up.port); + else + infof(data, "Establishing %s proxy tunnel to %s", + (ver == PROXY_HTTP_V2) ? "HTTP/2" : + (ver == PROXY_HTTP_V3) ? "HTTP/3" : "HTTP", + (*preq)->authority); + return CURLE_OK; +} + +CURLcode Curl_http_proxy_inspect_tunnel_response( + struct Curl_cfilter *cf, struct Curl_easy *data, + struct http_resp *resp, bool udp_tunnel, + proxy_inspect_result *presult) +{ + struct dynhds_entry *capsule_protocol = NULL; + struct dynhds_entry *auth_reply = NULL; + size_t i, header_count; + CURLcode result = CURLE_OK; + + DEBUGASSERT(resp); + + header_count = Curl_dynhds_count(&resp->headers); + if(udp_tunnel) + infof(data, "CONNECT-UDP Response Status %d", resp->status); + else + infof(data, "CONNECT Response Status %d", resp->status); + infof(data, "Response Headers (%zu total):", header_count); + for(i = 0; i < header_count; i++) { + struct dynhds_entry *entry = Curl_dynhds_getn(&resp->headers, i); + if(entry) + infof(data, " %s: %s", entry->name, entry->value); + } + + if(resp->status == 401) { + auth_reply = Curl_dynhds_cget(&resp->headers, "WWW-Authenticate"); + } + else if(resp->status == 407) { + auth_reply = Curl_dynhds_cget(&resp->headers, "Proxy-Authenticate"); + } + + if(auth_reply) { + CURL_TRC_CF(data, cf, "[0] CONNECT%s: fwd auth header '%s'", + udp_tunnel ? "-UDP" : "", auth_reply->value); + result = Curl_http_input_auth(data, resp->status == 407, + auth_reply->value); + if(result) + return result; + if(data->req.newurl) { + curlx_safefree(data->req.newurl); + *presult = PROXY_INSPECT_AUTH_RETRY; + return CURLE_OK; + } + } + + if(udp_tunnel) { + if(resp->status / 100 == 2) { + capsule_protocol = Curl_dynhds_cget(&resp->headers, + "capsule-protocol"); + if(capsule_protocol) { + if(!strncmp(capsule_protocol->value, "?1", 2) && + !capsule_protocol->value[2]) { + infof(data, "CONNECT-UDP tunnel established, response %d", + resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + failf(data, "Failed to establish CONNECT-UDP tunnel, response %d, " + "unsupported capsule-protocol value '%s'", + resp->status, capsule_protocol->value); + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; + } + else { + /* NOTE proxies may not set capsule protocol in the headers */ + infof(data, "CONNECT-UDP tunnel established, response %d " + "but no capsule-protocol header found", resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + } + else { + failf(data, "Failed to establish CONNECT-UDP tunnel, " + "response %d", resp->status); + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; + } + } + + if(resp->status / 100 == 2) { + infof(data, "CONNECT tunnel established, response %d", resp->status); + *presult = PROXY_INSPECT_OK; + return CURLE_OK; + } + + *presult = PROXY_INSPECT_FAILED; + return CURLE_COULDNT_CONNECT; +} + static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct cf_proxy_ctx *ctx = cf->ctx; CURLcode result; + bool udp_tunnel = TRNSPRT_IS_DGRAM(ctx->tunnel_transport); + const char *tunnel_type = udp_tunnel ? "CONNECT-UDP" : "CONNECT"; if(cf->connected) { *done = TRUE; return CURLE_OK; } - CURL_TRC_CF(data, cf, "connect"); + CURL_TRC_CF(data, cf, "%s", tunnel_type); connect_sub: - result = cf->next->cft->do_connect(cf->next, data, done); - if(result || !*done) - return result; + /* in case of h3_proxy, cf->next will be NULL initially */ + if(cf->next) { + result = cf->next->cft->do_connect(cf->next, data, done); + if(result || !*done) + return result; + } *done = FALSE; if(!ctx->sub_filter_installed) { - int httpversion = 0; - const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); + const char *alpn = NULL; + + /* in case of h3_proxy, cf->next will be NULL initially */ + if(cf->next) { + alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); + } if(alpn) - infof(data, "CONNECT: '%s' negotiated", alpn); - else - infof(data, "CONNECT: no ALPN negotiated"); + infof(data, "%s: '%s' negotiated", tunnel_type, alpn); + else if(!alpn) { + /* No ALPN, proxytype rules. Fake ALPN */ + infof(data, "%s: no ALPN negotiated", tunnel_type); + switch(ctx->proxytype) { + case CURLPROXY_HTTP_1_0: + alpn = "http/1.0"; + break; + case CURLPROXY_HTTPS2: + alpn = "h2"; + break; + case CURLPROXY_HTTPS3: + alpn = "h3"; + break; + default: + alpn = "http/1.1"; + break; + } + } - if(alpn && !strcmp(alpn, "http/1.0")) { + if(!strcmp(alpn, "http/1.0")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0"); - result = Curl_cf_h1_proxy_insert_after(cf, data); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer, 10, + udp_tunnel); if(result) goto out; - httpversion = 10; } - else if(!alpn || !strcmp(alpn, "http/1.1")) { - CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1"); - result = Curl_cf_h1_proxy_insert_after(cf, data); + else if(!strcmp(alpn, "http/1.1")) { + int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11; + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d", + httpversion % 10); + result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer, + httpversion, udp_tunnel); if(result) goto out; - /* Assume that without an ALPN, we are talking to an ancient one */ - httpversion = 11; } #ifdef USE_NGHTTP2 else if(!strcmp(alpn, "h2")) { CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); - result = Curl_cf_h2_proxy_insert_after(cf, data); + result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->tunnel_peer, + udp_tunnel); + if(result) + goto out; + } +#endif /* USE_NGHTTP2 */ +#if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + else if(!strcmp(alpn, "h3")) { + CURL_TRC_CF(data, cf, "installing subfilter for HTTP/3"); + result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->peer, ctx->peer, + ctx->tunnel_peer, + ctx->tunnel_transport); if(result) goto out; - httpversion = 20; } -#endif +#endif /* USE_PROXY_HTTP3 && USE_NGHTTP3 && USE_NGTCP2 && USE_OPENSSL */ else { - failf(data, "CONNECT: negotiated ALPN '%s' not supported", alpn); + failf(data, "%s: negotiated ALPN '%s' not supported", tunnel_type, alpn); result = CURLE_COULDNT_CONNECT; goto out; } ctx->sub_filter_installed = TRUE; - ctx->httpversion = httpversion; /* after we installed the filter "below" us, we call connect * on out sub-chain again. */ @@ -334,8 +653,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, } else { /* subchain connected and we had already installed the protocol filter. - * This means the protocol tunnel is established, we are done. - */ + * This means the protocol tunnel is established, we are done. */ DEBUGASSERT(ctx->sub_filter_installed); result = CURLE_OK; } @@ -348,14 +666,15 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, return result; } -CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) +static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) { + struct cf_proxy_ctx *ctx = cf->ctx; switch(query) { case CF_QUERY_HOST_PORT: - *pres1 = (int)cf->conn->http_proxy.port; - *((const char **)pres2) = cf->conn->http_proxy.host.name; + *pres1 = (int)ctx->tunnel_peer->port; + *((const char **)pres2) = ctx->tunnel_peer->hostname; return CURLE_OK; case CF_QUERY_ALPN_NEGOTIATED: { const char **palpn = pres2; @@ -371,22 +690,23 @@ CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, CURLE_UNKNOWN_OPTION; } -static void http_proxy_cf_destroy(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void cf_https_proxy_ctx_free(struct cf_proxy_ctx *ctx) { - struct cf_proxy_ctx *ctx = cf->ctx; - - CURL_TRC_CF(data, cf, "destroy"); - curlx_free(ctx); + if(ctx) { + Curl_peer_unlink(&ctx->peer); + Curl_peer_unlink(&ctx->tunnel_peer); + curlx_free(ctx); + } } -static void http_proxy_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) +static void http_proxy_cf_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) { - CURL_TRC_CF(data, cf, "close"); - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); + struct cf_proxy_ctx *ctx = cf->ctx; + if(ctx) { + CURL_TRC_CF(data, cf, "destroy"); + cf_https_proxy_ctx_free(ctx); + } } struct Curl_cftype Curl_cft_http_proxy = { @@ -395,7 +715,6 @@ struct Curl_cftype Curl_cft_http_proxy = { 0, http_proxy_cf_destroy, http_proxy_cf_connect, - http_proxy_cf_close, Curl_cf_def_shutdown, Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, @@ -404,22 +723,34 @@ struct Curl_cftype Curl_cft_http_proxy = { Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_http_proxy_query, + cf_http_proxy_query, }; CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, + uint8_t proxytype) { struct Curl_cfilter *cf; struct cf_proxy_ctx *ctx = NULL; CURLcode result; (void)data; + if(!peer || !tunnel_peer) + return CURLE_FAILED_INIT; + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } + Curl_peer_link(&ctx->peer, peer); + Curl_peer_link(&ctx->tunnel_peer, tunnel_peer); + ctx->proxytype = proxytype; + ctx->tunnel_transport = tunnel_transport; + result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); if(result) goto out; @@ -427,8 +758,18 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, Curl_conn_cf_insert_after(cf_at, cf); out: - curlx_free(ctx); + cf_https_proxy_ctx_free(ctx); return result; } +uint8_t Curl_http_proxy_transport(uint8_t proxytype) +{ + switch(proxytype) { + case CURLPROXY_HTTPS3: + return TRNSPRT_QUIC; + default: + return TRNSPRT_TCP; + } +} + #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ diff --git a/lib/http_proxy.h b/lib/http_proxy.h index 155b222edc0d..86c908809303 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -32,33 +32,51 @@ enum Curl_proxy_use { HEADER_SERVER, /* direct to server */ HEADER_PROXY, /* regular request to proxy */ - HEADER_CONNECT /* sending CONNECT to a proxy */ + HEADER_CONNECT, /* sending CONNECT to a proxy */ + HEADER_CONNECT_UDP /* sending CONNECT-UDP to a proxy */ }; -void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, - const char **phostname, - uint16_t *pport, bool *pipv6_ip); +/* HTTP version for proxy tunnel request creation */ +typedef enum { + PROXY_HTTP_V1 = 1, + PROXY_HTTP_V2 = 2, + PROXY_HTTP_V3 = 3 +} proxy_http_ver; -CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, - struct Curl_cfilter *cf, - struct Curl_easy *data, - int http_version_major); +/* Result from inspecting a proxy tunnel response */ +typedef enum { + PROXY_INSPECT_OK, /* Tunnel established */ + PROXY_INSPECT_FAILED, /* Tunnel failed */ + PROXY_INSPECT_AUTH_RETRY /* Retry with auth */ +} proxy_inspect_result; + +/* Create CONNECT or CONNECT-UDP request */ +CURLcode Curl_http_proxy_create_tunnel_request( + struct httpreq **preq, struct Curl_cfilter *cf, + struct Curl_easy *data, struct Curl_peer *dest, + proxy_http_ver ver, bool udp_tunnel); + +/* Inspect tunnel response for H2/H3 proxy (capsule-protocol, auth) */ +struct http_resp; +CURLcode Curl_http_proxy_inspect_tunnel_response( + struct Curl_cfilter *cf, struct Curl_easy *data, + struct http_resp *resp, bool udp_tunnel, + proxy_inspect_result *presult); /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600 * 1000) -CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2); - CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport, + uint8_t proxytype); extern struct Curl_cftype Curl_cft_http_proxy; -#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ +uint8_t Curl_http_proxy_transport(uint8_t proxytype); -#define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) || \ - ((t) == CURLPROXY_HTTPS2)) +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ #endif /* HEADER_CURL_HTTP_PROXY_H */ diff --git a/lib/httpsrr.c b/lib/httpsrr.c index 90fcb524e868..53647b81ed25 100644 --- a/lib/httpsrr.c +++ b/lib/httpsrr.c @@ -251,7 +251,7 @@ bool Curl_httpsrr_applicable(struct Curl_easy *data, return FALSE; return (!rr->target || !rr->target[0] || (rr->target[0] == '.' && !rr->target[1])) && - (!rr->port_set || rr->port == data->conn->remote_port); + (!rr->port_set || rr->port == data->conn->origin->port); } #ifdef USE_ARES diff --git a/lib/httpsrr.h b/lib/httpsrr.h index 28a790d17f07..2ee1beab3e76 100644 --- a/lib/httpsrr.h +++ b/lib/httpsrr.h @@ -31,7 +31,7 @@ #ifdef USE_HTTPSRR -#define CURL_MAXLEN_host_name 253 +#define CURL_MAXLEN_HOST_NAME 253 #define MAX_HTTPSRR_ALPNS 4 struct Curl_easy; diff --git a/lib/idn.c b/lib/idn.c index f2b954e2d03e..b26f251d975a 100644 --- a/lib/idn.c +++ b/lib/idn.c @@ -27,6 +27,7 @@ #include "curl_setup.h" #include "urldata.h" +#include "curlx/strparse.h" #include "idn.h" #ifdef USE_LIBIDN2 @@ -222,15 +223,24 @@ static CURLcode win32_ascii_to_idn(const char *in, char **out) */ bool Curl_is_ASCII_name(const char *hostname) { - /* get an UNSIGNED local version of the pointer */ - const unsigned char *ch = (const unsigned char *)hostname; - - if(!hostname) /* bad input, consider it ASCII! */ - return TRUE; + if(hostname) { + struct Curl_str s; + s.str = hostname; + s.len = strlen(hostname); + return Curl_is_ASCII_str(&s); + } + return TRUE; +} - while(*ch) { - if(*ch++ & 0x80) - return FALSE; +bool Curl_is_ASCII_str(struct Curl_str *s) +{ + if(s && s->len) { + const unsigned char *ch = (const unsigned char *)s->str; + size_t i; + for(i = 0; i < s->len; ++i) { + if(ch[i] & 0x80) + return FALSE; + } } return TRUE; } diff --git a/lib/idn.h b/lib/idn.h index 90d8e811b1a6..c73b870a7080 100644 --- a/lib/idn.h +++ b/lib/idn.h @@ -23,7 +23,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ + +struct Curl_str; +struct hostname; + bool Curl_is_ASCII_name(const char *hostname); +bool Curl_is_ASCII_str(struct Curl_str *s); + CURLcode Curl_idnconvert_hostname(struct hostname *host); #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) diff --git a/lib/if2ip.c b/lib/if2ip.c index b71254ada00e..fd34f204b5be 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -107,7 +107,7 @@ if2ip_result_t Curl_if2ip(int af, #endif if(getifaddrs(&head) >= 0) { - for(iface = head; iface != NULL; iface = iface->ifa_next) { + for(iface = head; iface; iface = iface->ifa_next) { if(iface->ifa_addr) { if(iface->ifa_addr->sa_family == af) { if(curl_strequal(iface->ifa_name, interf)) { diff --git a/lib/if2ip.h b/lib/if2ip.h index 12fdaabd736a..dc79c383b793 100644 --- a/lib/if2ip.h +++ b/lib/if2ip.h @@ -54,7 +54,7 @@ if2ip_result_t Curl_if2ip(int af, #ifdef __INTERIX -/* Nedelcho Stanev's work-around for SFU 3.0 */ +/* Nedelcho Stanev's workaround for SFU 3.0 */ struct ifreq { #define IFNAMSIZ 16 #define IFHWADDRLEN 6 diff --git a/lib/imap.c b/lib/imap.c index 5ef2a2cb2189..976c9f573e7c 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -555,7 +555,8 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, bool ssldone = FALSE; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler */ @@ -565,7 +566,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!imapc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { imapc->ssldone = ssldone; /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */ @@ -597,15 +598,15 @@ static CURLcode imap_perform_login(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!conn->creds) { imap_state(data, imapc, IMAP_STOP); return result; } /* Make sure the username and password are in the correct atom format */ - user = imap_atom(conn->user, FALSE); - passwd = imap_atom(conn->passwd, FALSE); + user = imap_atom(Curl_creds_user(conn->creds), FALSE); + passwd = imap_atom(Curl_creds_passwd(conn->creds), FALSE); /* Send the LOGIN command */ result = imap_sendf(data, imapc, "LOGIN %s %s", user ? user : "", @@ -712,7 +713,6 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data, /* Calculate the SASL login details */ result = Curl_sasl_start(&imapc->sasl, data, (bool)imapc->ir_supported, &progress); - if(!result) { if(progress == SASL_INPROGRESS) imap_state(data, imapc, IMAP_AUTHENTICATE); @@ -911,12 +911,12 @@ static CURLcode imap_perform_append(struct Curl_easy *data, if(data->set.upload_flags) { int i; struct ulbits ulflag[] = { - {CURLULFLAG_ANSWERED, "Answered"}, - {CURLULFLAG_DELETED, "Deleted"}, - {CURLULFLAG_DRAFT, "Draft"}, - {CURLULFLAG_FLAGGED, "Flagged"}, - {CURLULFLAG_SEEN, "Seen"}, - {0, NULL} + { CURLULFLAG_ANSWERED, "Answered" }, + { CURLULFLAG_DELETED, "Deleted" }, + { CURLULFLAG_DRAFT, "Draft" }, + { CURLULFLAG_FLAGGED, "Flagged" }, + { CURLULFLAG_SEEN, "Seen" }, + { 0, NULL } }; result = CURLE_OUT_OF_MEMORY; @@ -1044,7 +1044,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, /* Extract the word */ for(wordlen = 0; line[wordlen] && !ISBLANK(line[wordlen]) && - !ISNEWLINE(line[wordlen]);) + !ISNEWLINE(line[wordlen]);) wordlen++; /* Does the server support the STARTTLS capability? */ @@ -1076,7 +1076,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data, line += wordlen; } } - else if(data->set.use_ssl && !Curl_xfer_is_secure(data)) { + else if(data->set.use_ssl && !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ @@ -1189,9 +1189,9 @@ static bool is_custom_fetch_listing_match(const char *params) return FALSE; } if(*params == ':') - return true; + return TRUE; if(*params == ',') - return true; + return TRUE; return FALSE; } @@ -1796,7 +1796,7 @@ static CURLcode imap_parse_url_options(struct connectdata *conn, static CURLcode imap_parse_url_path(struct Curl_easy *data, struct IMAP *imap) { - /* The imap struct is already initialised in imap_connect() */ + /* The imap struct is already initialized in imap_connect() */ CURLcode result = CURLE_OK; const char *begin = &data->state.up.path[1]; /* skip leading slash */ const char *ptr = begin; diff --git a/lib/ldap.c b/lib/ldap.c index 16c93eeca253..a6a69eb7f1e7 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -45,7 +45,7 @@ #ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */ # include -/* Undefine indirect symbols conflicting with BoringSSL/AWS-LC. */ +/* Undefine indirect symbols conflicting with AWS-LC/BoringSSL. */ # undef X509_NAME # undef X509_EXTENSIONS # undef PKCS7_ISSUER_AND_SERIAL @@ -230,6 +230,27 @@ static ULONG ldap_win_bind(struct Curl_easy *data, LDAP *server, } #endif /* USE_WIN32_LDAP */ +static bool ldap_value_needs_base64(const char *attr, size_t attr_len, + const BerValue *val) +{ + ber_len_t j; + + if((attr_len > 7) && curl_strequal(";binary", attr + attr_len - 7)) + return TRUE; + + /* check for leading or trailing whitespace */ + if(val->bv_len && (ISBLANK(val->bv_val[0]) || + ISBLANK(val->bv_val[val->bv_len - 1]))) + return TRUE; + + /* check for unprintable characters */ + for(j = 0; j < val->bv_len; j++) + if(!ISPRINT(val->bv_val[j])) + return TRUE; + + return FALSE; +} + static CURLcode ldap_do(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; @@ -242,20 +263,21 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) struct connectdata *conn = data->conn; int ldap_proto = LDAP_VERSION3; int ldap_ssl = 0; - char *val_b64 = NULL; - size_t val_b64_sz = 0; #ifdef LDAP_OPT_NETWORK_TIMEOUT - struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */ + struct timeval ldap_timeout = { 10, 0 }; /* 10s connection/search timeout */ #endif #ifdef USE_WIN32_LDAP TCHAR *host = NULL; #else char *host = NULL; #endif - char *user = NULL; - char *passwd = NULL; + const char *user = Curl_creds_has_user(data->state.creds) ? + data->state.creds->user : NULL; + const char *passwd = Curl_creds_has_passwd(data->state.creds) ? + data->state.creds->passwd : NULL; struct ip_quadruple ipquad; bool is_ipv6; + BerElement *ber = NULL; *done = TRUE; /* unconditionally */ infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d", @@ -284,21 +306,16 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_ssl ? "encrypted" : "cleartext"); #ifdef USE_WIN32_LDAP - host = curlx_convert_UTF8_to_tchar(conn->host.name); + host = curlx_convert_UTF8_to_tchar(conn->origin->hostname); if(!host) { result = CURLE_OUT_OF_MEMORY; goto quit; } #else - host = conn->host.name; + host = conn->origin->hostname; #endif - if(data->state.aptr.user) { - user = conn->user; - passwd = conn->passwd; - } - #ifdef USE_WIN32_LDAP if(ldap_ssl) server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1); @@ -307,7 +324,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) 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); + conn->origin->user_hostname, ipquad.remote_port); result = CURLE_COULDNT_CONNECT; goto quit; } @@ -317,6 +334,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #endif ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); + /* Do not chase referrals. */ + ldap_set_option(server, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); + if(ldap_ssl) { #ifdef HAVE_LDAP_SSL #ifdef USE_WIN32_LDAP @@ -328,8 +348,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #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.primary.cert_type && + !curl_strequal(data->set.ssl.primary.cert_type, "PEM")) { failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type"); result = CURLE_SSL_CERTPROBLEM; goto quit; @@ -427,7 +447,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) for(entryIterator = ldap_first_entry(server, ldapmsg); entryIterator; entryIterator = ldap_next_entry(server, entryIterator), num++) { - BerElement *ber = NULL; #ifdef USE_WIN32_LDAP TCHAR *attribute; #else @@ -437,23 +456,17 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) /* Get the DN and write it to the client */ { - char *name; + char *name = NULL; size_t name_len = 0; #ifdef USE_WIN32_LDAP TCHAR *dn = ldap_get_dn(server, entryIterator); - name = curlx_convert_tchar_to_UTF8(dn); - if(!name) { - ldap_memfree(dn); - - result = CURLE_OUT_OF_MEMORY; - - goto quit; - } + if(dn) + name = curlx_convert_tchar_to_UTF8(dn); #else char *dn = name = ldap_get_dn(server, entryIterator); #endif if(!name) - result = CURLE_FAILED_INIT; + result = dn ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; else { name_len = strlen(name); result = Curl_client_write(data, CLIENTWRITE_BODY, "DN: ", 4); @@ -477,11 +490,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) #ifdef USE_WIN32_LDAP char *attr = curlx_convert_tchar_to_UTF8(attribute); if(!attr) { - if(ber) - ber_free(ber, 0); - + ldap_memfree(attribute); result = CURLE_OUT_OF_MEMORY; - goto quit; } #else @@ -491,15 +501,12 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) vals = ldap_get_values_len(server, entryIterator, attribute); if(vals) { - for(i = 0; (vals[i] != NULL); i++) { + for(i = 0; vals[i]; i++) { result = Curl_client_write(data, CLIENTWRITE_BODY, "\t", 1); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } @@ -508,25 +515,21 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } - result = Curl_client_write(data, CLIENTWRITE_BODY, ": ", 2); + result = Curl_client_write(data, CLIENTWRITE_BODY, ":", 1); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } - if((attr_len > 7) && - curl_strequal(";binary", attr + (attr_len - 7))) { + if(ldap_value_needs_base64(attr, attr_len, vals[i])) { + char *val_b64 = NULL; + size_t val_b64_sz = 0; + /* Binary attribute, encode to base64. */ if(vals[i]->bv_len) { result = curlx_base64_encode((uint8_t *)vals[i]->bv_val, @@ -536,38 +539,48 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) 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); + result = Curl_client_write(data, CLIENTWRITE_BODY, ": ", 2); + if(result) { + curlx_free(val_b64); + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + goto quit; + } + + if(val_b64_sz) { + result = Curl_client_write(data, CLIENTWRITE_BODY, val_b64, + val_b64_sz); + if(result) { 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; - } + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); + goto quit; } } + + curlx_free(val_b64); } else { - result = Curl_client_write(data, CLIENTWRITE_BODY, vals[i]->bv_val, - vals[i]->bv_len); + result = Curl_client_write(data, CLIENTWRITE_BODY, " ", 1); if(result) { ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); + goto quit; + } + result = Curl_client_write(data, CLIENTWRITE_BODY, + vals[i]->bv_val, vals[i]->bv_len); + if(result) { + ldap_value_free_len(vals); + FREE_ON_WINLDAP(attr); + ldap_memfree(attribute); goto quit; } } @@ -577,9 +590,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) ldap_value_free_len(vals); FREE_ON_WINLDAP(attr); ldap_memfree(attribute); - if(ber) - ber_free(ber, 0); - goto quit; } } @@ -597,11 +607,15 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) goto quit; } - if(ber) + if(ber) { ber_free(ber, 0); + ber = NULL; + } } quit: + if(ber) + ber_free(ber, 0); if(ldapmsg) { ldap_msgfree(ldapmsg); LDAP_TRACE(("Received %d entries\n", num)); @@ -681,8 +695,8 @@ static size_t num_entries(const char *s) * Syntax: * ldap://:/???? * - * already known from 'conn->host.name'. - * already known from 'conn->remote_port'. + * already known from 'conn->origin->hostname'. + * already known from 'conn->origin->port'. * extract the rest from 'data->state.path+1'. All fields are optional. * e.g. * ldap://:/??? @@ -708,8 +722,8 @@ static curl_ldap_num_t ldap_url_parse2_low(struct Curl_easy *data, return LDAP_INVALID_SYNTAX; ludp->lud_scope = LDAP_SCOPE_BASE; - ludp->lud_port = conn->remote_port; - ludp->lud_host = conn->host.name; + ludp->lud_port = conn->origin->port; + ludp->lud_host = conn->origin->hostname; /* Duplicate the path */ p = path = curlx_strdup(data->state.up.path + 1); diff --git a/lib/llist.c b/lib/llist.c index 3ec85e4a2ce2..ce0c59cc50ed 100644 --- a/lib/llist.c +++ b/lib/llist.c @@ -200,7 +200,7 @@ 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) +struct Curl_llist_node *Curl_llist_head(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -213,8 +213,8 @@ struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list) @unittest 1300 */ -UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list); -UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list) +UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list); +UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -223,7 +223,7 @@ UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list) #endif /* Curl_llist_count() returns a size_t the number of nodes in the list */ -size_t Curl_llist_count(struct Curl_llist *list) +size_t Curl_llist_count(const struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -231,7 +231,7 @@ size_t Curl_llist_count(struct Curl_llist *list) } /* Curl_node_elem() returns the custom data from a Curl_llist_node */ -void *Curl_node_elem(struct Curl_llist_node *n) +void *Curl_node_elem(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -240,7 +240,7 @@ void *Curl_node_elem(struct Curl_llist_node *n) /* 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) +struct Curl_llist_node *Curl_node_next(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -253,8 +253,10 @@ struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n) @unittest 1300 */ -UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n); -UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n) +UNITTEST struct Curl_llist_node *llist_node_prev( + const struct Curl_llist_node *n); +UNITTEST struct Curl_llist_node *llist_node_prev( + const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); @@ -262,7 +264,7 @@ UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n) } #endif -struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n) +struct Curl_llist *Curl_node_llist(const struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(!n->_list || n->_init == NODEINIT); diff --git a/lib/llist.h b/lib/llist.h index 28e958d5c5e9..de4adc972f47 100644 --- a/lib/llist.h +++ b/lib/llist.h @@ -60,13 +60,13 @@ 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); +struct Curl_llist_node *Curl_llist_head(const 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); +size_t Curl_llist_count(const struct Curl_llist *list); /* Curl_node_elem() returns the custom data from a Curl_llist_node */ -void *Curl_node_elem(struct Curl_llist_node *n); +void *Curl_node_elem(const struct Curl_llist_node *n); /* Remove the node from the list and return the custom data * from a Curl_llist_node. Does NOT invoke a registered `dtor`. */ @@ -74,9 +74,9 @@ 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); +struct Curl_llist_node *Curl_node_next(const 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); +struct Curl_llist *Curl_node_llist(const struct Curl_llist_node *n); #endif /* HEADER_CURL_LLIST_H */ diff --git a/lib/md4.c b/lib/md4.c index 0213483ad30c..e030ffac30c2 100644 --- a/lib/md4.c +++ b/lib/md4.c @@ -158,6 +158,7 @@ static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) #elif defined(USE_GNUTLS) #include +#include typedef struct md4_ctx my_md4_ctx; @@ -175,7 +176,11 @@ static void my_md4_update(my_md4_ctx *ctx, static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + md4_digest(ctx, digest); +#else md4_digest(ctx, MD4_DIGEST_SIZE, digest); +#endif } #else diff --git a/lib/md5.c b/lib/md5.c index 4dd0d7c27859..1f1b4f8ad610 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -47,6 +47,7 @@ #ifdef USE_GNUTLS #include +#include typedef struct md5_ctx my_md5_ctx; @@ -64,7 +65,11 @@ static void my_md5_update(void *ctx, static void my_md5_final(unsigned char *digest, void *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + md5_digest(ctx, digest); +#else md5_digest(ctx, 16, digest); +#endif } #elif defined(USE_OPENSSL) && \ @@ -116,7 +121,7 @@ static void my_md5_final(unsigned char *digest, void *ctx) } #elif defined(USE_MBEDTLS) && \ - defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 /* mbedTLS 4+ */ + defined(PSA_WANT_ALG_MD5) && PSA_WANT_ALG_MD5 #include typedef psa_hash_operation_t my_md5_ctx; diff --git a/lib/memdebug.c b/lib/memdebug.c index 6eda7a8236ba..b6af2c6d3528 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -269,7 +269,7 @@ char *curl_dbg_strdup(const char *str, int line, const char *source) char *mem; size_t len; - DEBUGASSERT(str != NULL); + DEBUGASSERT(str); if(countcheck("strdup", line, source)) return NULL; @@ -294,7 +294,7 @@ wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source) wchar_t *mem; size_t wsiz, bsiz; - DEBUGASSERT(str != NULL); + DEBUGASSERT(str); if(countcheck("wcsdup", line, source)) return NULL; @@ -510,7 +510,7 @@ int curl_dbg_fclose(FILE *file, int line, const char *source) { int res; - DEBUGASSERT(file != NULL); + DEBUGASSERT(file); if(source) curl_dbg_log("FILE %s:%d fclose(%p)\n", source, line, (void *)file); diff --git a/lib/mime.c b/lib/mime.c index b7e51aae4e96..077e599e43f1 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -1123,7 +1123,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, curl_mime *mime; curl_mimepart *d; const curl_mimepart *s; - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; DEBUGASSERT(dst); @@ -1132,66 +1132,67 @@ 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); + result = curl_mime_data(dst, src->data, (size_t)src->datasize); break; case MIMEKIND_FILE: - res = curl_mime_filedata(dst, src->data); + result = curl_mime_filedata(dst, src->data); /* Do not abort duplication if file is not readable. */ - if(res == CURLE_READ_ERROR) - res = CURLE_OK; + if(result == CURLE_READ_ERROR) + result = CURLE_OK; break; case MIMEKIND_CALLBACK: - res = curl_mime_data_cb(dst, src->datasize, src->readfunc, - src->seekfunc, src->freefunc, src->arg); + result = curl_mime_data_cb(dst, src->datasize, src->readfunc, + src->seekfunc, src->freefunc, src->arg); break; case MIMEKIND_MULTIPART: /* No one knows about the cloned subparts, thus always attach ownership to the part. */ mime = curl_mime_init(data); - res = mime ? curl_mime_subparts(dst, mime) : CURLE_OUT_OF_MEMORY; + result = 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; !result && s; + s = s->nextpart) { d = curl_mime_addpart(mime); - res = d ? Curl_mime_duppart(data, d, s) : CURLE_OUT_OF_MEMORY; + result = d ? Curl_mime_duppart(data, d, s) : CURLE_OUT_OF_MEMORY; } break; default: /* Invalid kind: should not occur. */ DEBUGF(infof(data, "invalid MIMEKIND* attempt")); - res = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ + result = CURLE_BAD_FUNCTION_ARGUMENT; /* Internal error? */ break; } /* Duplicate headers. */ - if(!res && src->userheaders) { + if(!result && src->userheaders) { struct curl_slist *hdrs = Curl_slist_duplicate(src->userheaders); if(!hdrs) - res = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else { /* No one but this procedure knows about the new header list, so always take ownership. */ - res = curl_mime_headers(dst, hdrs, TRUE); - if(res) + result = curl_mime_headers(dst, hdrs, TRUE); + if(result) curl_slist_free_all(hdrs); } } - if(!res) { + if(!result) { /* Duplicate other fields. */ dst->encoder = src->encoder; - res = curl_mime_type(dst, src->mimetype); + result = curl_mime_type(dst, src->mimetype); } - if(!res) - res = curl_mime_name(dst, src->name); - if(!res) - res = curl_mime_filename(dst, src->filename); + if(!result) + result = curl_mime_name(dst, src->name); + if(!result) + result = curl_mime_filename(dst, src->filename); /* If an error occurred, rollback. */ - if(res) + if(result) Curl_mime_cleanpart(dst); - return res; + return result; } /* @@ -1201,9 +1202,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, /* Create a mime handle. */ curl_mime *curl_mime_init(void *easy) { - curl_mime *mime; - - mime = (curl_mime *)curlx_malloc(sizeof(*mime)); + curl_mime *mime = curlx_malloc(sizeof(*mime)); if(mime) { mime->parent = NULL; @@ -1240,7 +1239,7 @@ curl_mimepart *curl_mime_addpart(curl_mime *mime) if(!mime) return NULL; - part = (curl_mimepart *)curlx_malloc(sizeof(*part)); + part = curlx_malloc(sizeof(*part)); if(part) { Curl_mime_initpart(part); @@ -1692,6 +1691,52 @@ static bool content_type_match(const char *contenttype, return FALSE; } +static CURLcode add_content_disposition(struct Curl_easy *data, + curl_mimepart *part, + const char *disposition, + const char *contenttype, + enum mimestrategy strategy) +{ + if(!disposition) + if(part->filename || part->name || + (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) + disposition = DISPOSITION_DEFAULT; + if(disposition && curl_strequal(disposition, "attachment") && + !part->name && !part->filename) + disposition = NULL; + if(disposition) { + CURLcode result = CURLE_OK; + char *name = NULL; + char *filename = NULL; + + if(part->name) { + name = escape_string(data, part->name, strategy); + if(!name) + return CURLE_OUT_OF_MEMORY; + } + if(part->filename) { + filename = escape_string(data, part->filename, strategy); + if(!filename) + result = CURLE_OUT_OF_MEMORY; + } + if(!result) + result = Curl_mime_add_header(&part->curlheaders, + "Content-Disposition: %s%s%s%s%s%s%s", + disposition, + name ? "; name=\"" : "", + name ? name : "", + name ? "\"" : "", + filename ? "; filename=\"" : "", + filename ? filename : "", + filename ? "\"" : ""); + curlx_safefree(name); + curlx_safefree(filename); + if(result) + return result; + } + return CURLE_OK; +} + CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, curl_mimepart *part, const char *contenttype, @@ -1750,42 +1795,10 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, /* Issue content-disposition header only if not already set by caller. */ if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) { - if(!disposition) - if(part->filename || part->name || - (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) - disposition = DISPOSITION_DEFAULT; - if(disposition && curl_strequal(disposition, "attachment") && - !part->name && !part->filename) - disposition = NULL; - if(disposition) { - char *name = NULL; - char *filename = NULL; - - if(part->name) { - name = escape_string(data, part->name, strategy); - if(!name) - result = CURLE_OUT_OF_MEMORY; - } - if(!result && part->filename) { - filename = escape_string(data, part->filename, strategy); - if(!filename) - result = CURLE_OUT_OF_MEMORY; - } - if(!result) - result = Curl_mime_add_header(&part->curlheaders, - "Content-Disposition: %s%s%s%s%s%s%s", - disposition, - name ? "; name=\"" : "", - name ? name : "", - name ? "\"" : "", - filename ? "; filename=\"" : "", - filename ? filename : "", - filename ? "\"" : ""); - curlx_safefree(name); - curlx_safefree(filename); - if(result) - return result; - } + result = add_content_disposition(data, part, disposition, + contenttype, strategy); + if(result) + return result; } /* Issue Content-Type header. */ @@ -1896,7 +1909,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data, /* 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", - blen, ctx->error_result); + blen, (int)ctx->error_result); *pnread = 0; *peos = FALSE; return ctx->error_result; @@ -1927,7 +1940,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data, else if(blen <= 4) { /* Curl_mime_read() may go into an infinite loop when reading * via a base64 encoder, as it stalls when the read buffer is too small - * to contain a complete 3 byte encoding. Read into a larger buffer + * to contain a complete 3-byte encoding. Read into a larger buffer * and use that until empty. */ CURL_TRC_READ(data, "cr_mime_read(len=%zu), small read, using tmp", blen); nread = Curl_mime_read(tmp, 1, sizeof(tmp), ctx->part); @@ -2013,8 +2026,8 @@ static CURLcode cr_mime_read(struct Curl_easy *data, } CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T - ", read=%" FMT_OFF_T ") -> %d, %zu, %d", - blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); + ", read=%" FMT_OFF_T ") -> %d, %zu, %d", blen, + ctx->total_len, ctx->read_len, (int)result, *pnread, *peos); return result; } diff --git a/lib/mprintf.c b/lib/mprintf.c index 6eaea66b7055..230b554191e4 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -244,7 +244,7 @@ static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar, fmt += 2; } else { -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) +#if SIZEOF_CURL_OFF_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; @@ -267,14 +267,14 @@ static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar, case 'z': /* the code below generates a warning if -Wunreachable-code is used */ -#if (SIZEOF_SIZE_T > SIZEOF_LONG) +#if SIZEOF_SIZE_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; #endif break; case 'O': -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) +#if SIZEOF_CURL_OFF_T > SIZEOF_LONG flags |= FLAGS_LONGLONG; #else flags |= FLAGS_LONG; @@ -423,7 +423,6 @@ static bool parse_conversion(const char f, unsigned int *flagp, return FALSE; } - static int parsefmt(const char *format, struct outsegment *out, struct va_input *in, @@ -968,7 +967,6 @@ 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(), sent to the stream() function in the second argument */ /* function pointer called for each output character */ @@ -1075,9 +1073,8 @@ static int formatf(void *userp, /* untouched by format(), sent to the /* Answer the count of characters written. */ if(p.flags & FLAGS_LONGLONG) *(int64_t *)iptr->val.ptr = (int64_t)done; - else - if(p.flags & FLAGS_LONG) - *(long *)iptr->val.ptr = (long)done; + else if(p.flags & FLAGS_LONG) + *(long *)iptr->val.ptr = (long)done; else if(!(p.flags & FLAGS_SHORT)) *(int *)iptr->val.ptr = done; else diff --git a/lib/mqtt.c b/lib/mqtt.c index 84fd272e21e7..8482477b9725 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -280,11 +280,9 @@ 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 size_t ulen = strlen(username); - /* extracting password from request */ - const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : ""; - const size_t plen = strlen(passwd); + struct Curl_creds *creds = data->state.creds; + const size_t ulen = creds ? strlen(creds->user) : 0; + const size_t plen = creds ? strlen(creds->passwd) : 0; 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 string to be added on the payload. Refer to spec 1.5.2 and 1.5.4 */ @@ -326,7 +324,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) if(ulen) { start_pwd += 2; - rc = add_user(username, ulen, + rc = add_user(creds->user, ulen, (unsigned char *)packet, start_user, remain_pos); if(rc) { failf(data, "Username too long: [%zu]", ulen); @@ -337,7 +335,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) /* if passwd was provided, add it to the packet */ if(plen) { - rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos); + rc = add_passwd(creds->passwd, plen, packet, start_pwd, remain_pos); if(rc) { failf(data, "Password too long: [%zu]", plen); result = CURLE_WEIRD_SERVER_REPLY; @@ -351,8 +349,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data) end: if(packet) curlx_free(packet); - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); + Curl_creds_unlink(&data->state.creds); return result; } @@ -427,7 +424,7 @@ static CURLcode mqtt_verify_connack(struct Curl_easy *data) if(ptr[0] != 0x00 || ptr[1] != 0x00) { failf(data, "Expected %02x%02x but got %02x%02x", - 0x00, 0x00, ptr[0], ptr[1]); + 0x00U, 0x00U, (unsigned char)ptr[0], (unsigned char)ptr[1]); curlx_dyn_reset(&mq->recvbuf); return CURLE_WEIRD_SERVER_REPLY; } @@ -605,9 +602,9 @@ static CURLcode mqtt_publish(struct Curl_easy *data) return result; } -/* return 0 on success, non-zero on error */ -static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, - size_t buflen) +/* return FALSE on success, TRUE on error */ +static bool mqtt_decode_len(size_t *lenp, const unsigned char *buf, + size_t buflen) { size_t len = 0; size_t mult = 1; @@ -616,14 +613,17 @@ static int mqtt_decode_len(size_t *lenp, const unsigned char *buf, for(i = 0; (i < buflen) && (encoded & 128); i++) { if(i == 4) - return 1; /* bad size */ + return TRUE; /* bad size */ encoded = buf[i]; len += (encoded & 127) * mult; mult *= 128; } + if(encoded & 128) + /* truncated size */ + return TRUE; *lenp = len; - return 0; + return FALSE; } #if defined(DEBUGBUILD) && defined(CURLVERBOSE) @@ -774,7 +774,7 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done) result = mqtt_connect(data); if(result) { - failf(data, "Error %d sending MQTT CONNECT request", result); + failf(data, "Error %d sending MQTT CONNECT request", (int)result); return result; } mqstate(data, MQTT_FIRST, MQTT_CONNACK); @@ -892,6 +892,24 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) break; } mq->npacket = 0; + /* PINGRESP and DISCONNECT must have remaining_length == 0 and + * reserved bits (low nibble) must be zero per MQTT 3.1.1 + * sections 2.2.2, 3.13.1 and 3.14.1. Reject before state + * dispatch to prevent nextstate confusion. */ + { + const unsigned char type = mq->firstbyte & 0xF0; + const unsigned char reserved = mq->firstbyte & 0x0F; + if((type == MQTT_MSG_DISCONNECT || type == MQTT_MSG_PINGRESP) && + (mq->remaining_length || reserved)) { + failf(data, + "Broker sent malformed %s " + "(remaining_length=%zu, header byte=0x%02x)", + type == MQTT_MSG_DISCONNECT ? "DISCONNECT" : "PINGRESP", + mq->remaining_length, mq->firstbyte); + result = CURLE_WEIRD_SERVER_REPLY; + break; + } + } if(mq->remaining_length) { mqstate(data, mqtt->nextstate, MQTT_NOSTATE); break; diff --git a/lib/multi.c b/lib/multi.c index 7520253d702c..d6ae111d8ee3 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -78,8 +78,8 @@ /* On a debug build, we want to fail hard on multi handles that * are not NULL, but no longer have the MAGIC touch. This gives * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_MULTI_HANDLE(x) \ - (((x) && (x)->magic == CURL_MULTI_HANDLE)? TRUE: \ +#define GOOD_MULTI_HANDLE(x) \ + (((x) && (x)->magic == CURL_MULTI_HANDLE) ? TRUE : \ (DEBUGASSERT(!(x)), FALSE)) #else #define GOOD_MULTI_HANDLE(x) \ @@ -106,20 +106,56 @@ static const struct curltime *multi_now(struct Curl_multi *multi) return &multi->now; } -/* function pointer called once when switching TO a state */ -typedef void (*init_multistate_func)(struct Curl_easy *data); +/* function pointer called once when entering a state */ +typedef void (*mstate_enter_func)(struct Curl_easy *data, + CURLMstate from_state); + +static void mstate_enter_connect(struct Curl_easy *data, + CURLMstate from_state) +{ + (void)from_state; + Curl_init_CONNECT(data); +} -/* called in DID state, before PERFORMING state */ -static void before_perform(struct Curl_easy *data) +static void mstate_enter_did(struct Curl_easy *data, + CURLMstate from_state) { + (void)from_state; data->req.chunk = FALSE; Curl_pgrsTime(data, TIMER_PRETRANSFER); + if(!CURL_REQ_WANT_SEND(data)) + Curl_pgrsTime(data, TIMER_POSTRANSFER); +} + +static void mstate_enter_done(struct Curl_easy *data, + CURLMstate from_state) +{ + (void)from_state; + CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); } -static void init_completed(struct Curl_easy *data) +static void mstate_enter_completed(struct Curl_easy *data, + CURLMstate from_state) { - /* this is a completed transfer */ + /* we sometimes directly jump to COMPLETED, trigger things + * we then missed. */ + if(from_state < MSTATE_DID) { + Curl_pgrsTime(data, TIMER_PRETRANSFER); + Curl_pgrsTime(data, TIMER_POSTRANSFER); + Curl_pgrsTime(data, TIMER_STARTTRANSFER); + } + Curl_pgrsCompleted(data); + if(from_state < MSTATE_DONE) + CURLM_NTFY(data, CURLMNOTIFY_EASY_DONE); + /* changing to COMPLETED means it is in process and needs to go */ + 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_uint32_bset_empty(&data->multi->process)) { + /* free the transfer buffer when we have no more active transfers */ + multi_xfer_bufs_free(data->multi); + } /* Important: reset the conn pointer so that we do not point to memory that could be freed anytime */ Curl_detach_connection(data); @@ -134,23 +170,23 @@ static void mstate(struct Curl_easy *data, CURLMstate state ) { CURLMstate oldstate = data->mstate; - static const init_multistate_func finit[MSTATE_LAST] = { - NULL, /* INIT */ - NULL, /* PENDING */ - NULL, /* SETUP */ - Curl_init_CONNECT, /* CONNECT */ - NULL, /* CONNECTING */ - NULL, /* PROTOCONNECT */ - NULL, /* PROTOCONNECTING */ - NULL, /* DO */ - NULL, /* DOING */ - NULL, /* DOING_MORE */ - before_perform, /* DID */ - NULL, /* PERFORMING */ - NULL, /* RATELIMITING */ - NULL, /* DONE */ - init_completed, /* COMPLETED */ - NULL /* MSGSENT */ + static const mstate_enter_func state_enter[MSTATE_LAST] = { + NULL, /* INIT */ + NULL, /* PENDING */ + NULL, /* SETUP */ + mstate_enter_connect, /* CONNECT */ + NULL, /* CONNECTING */ + NULL, /* PROTOCONNECT */ + NULL, /* PROTOCONNECTING */ + NULL, /* DO */ + NULL, /* DOING */ + NULL, /* DOING_MORE */ + mstate_enter_did, /* DID */ + NULL, /* PERFORMING */ + NULL, /* RATELIMITING */ + mstate_enter_done, /* DONE */ + mstate_enter_completed, /* COMPLETED */ + NULL /* MSGSENT */ }; if(oldstate == state) @@ -166,32 +202,8 @@ static void mstate(struct Curl_easy *data, CURLMstate state /* really switching state */ data->mstate = state; - 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_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_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 */ - if(finit[state]) - finit[state](data); + if(state_enter[state]) + state_enter[state](data, oldstate); } #ifndef DEBUGBUILD @@ -208,7 +220,7 @@ static void ph_freeentry(void *p) /* 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); + DEBUGASSERT(!p); } /* @@ -404,7 +416,7 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, if(capacity < max_capacity) { /* We want `multi->xfers` to have "sufficient" free rows, so that we do - * have to reuse the `mid` from a removed easy right away. + * not 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). */ @@ -420,7 +432,7 @@ 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 + /* make it a 64 multiple, since our bitsets grow by that and * small (easy_multi) grows to at least 64 on first resize. */ new_size = (((used + min_unused) + 63) / 64) * 64; } @@ -519,6 +531,8 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) /* set the easy handle */ multistate(data, MSTATE_INIT); + /* not yet passed INIT state */ + data->state.really_alive = FALSE; #ifdef USE_LIBPSL /* Do the same for PSL. */ @@ -558,12 +572,6 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) 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_uint32_tbl_count(&multi->xfers)); @@ -839,6 +847,12 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */ if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid)) --multi->xfers_alive; + if(data->state.really_alive) { + data->state.really_alive = FALSE; + --multi->xfers_really_alive; + if(!multi->xfers_really_alive) + (void)multi_assess_wakeup(multi); + } Curl_wildcard_dtor(&data->wildcard); @@ -1139,7 +1153,9 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, /* 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) { + data->multi->xfers_really_alive) { + CURL_TRC_M(data, "adding wakeup, %u xfers really alive", + data->multi->xfers_really_alive); result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); } #endif @@ -1192,7 +1208,8 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, break; default: - failf(data, "multi_getsock: unexpected multi state %d", data->mstate); + failf(data, "multi_getsock: unexpected multi state %d", + (int)data->mstate); DEBUGASSERT(0); break; } @@ -1201,7 +1218,7 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, if(result) { if(result == CURLE_OUT_OF_MEMORY) return CURLM_OUT_OF_MEMORY; - failf(data, "error determining pollset: %d", result); + failf(data, "error determining pollset: %d", (int)result); return CURLM_INTERNAL_ERROR; } @@ -1707,9 +1724,13 @@ CURLMcode curl_multi_wakeup(CURLM *m) */ static bool multi_ischanged(struct Curl_multi *multi, bool clear) { - bool retval = (bool)multi->recheckstate; - if(clear) - multi->recheckstate = FALSE; + bool retval = FALSE; + DEBUGASSERT(multi); + if(multi) { + retval = (bool)multi->recheckstate; + if(clear) + multi->recheckstate = FALSE; + } return retval; } @@ -1773,16 +1794,16 @@ static CURLcode multi_do(struct Curl_easy *data, bool *done) * 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! + * 'complete' can return DOMORE_INCOMPLETE, DOMORE_DONE or DOMORE_GOBACK + * (to DOING state when there is more work to do) */ -static CURLcode multi_do_more(struct Curl_easy *data, int *complete) +static CURLcode multi_do_more(struct Curl_easy *data, domore *complete) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - *complete = 0; + *complete = DOMORE_INCOMPLETE; if(conn->scheme->run->do_more) result = conn->scheme->run->do_more(data, complete); @@ -2000,9 +2021,9 @@ static CURLcode mspeed_check(struct Curl_easy *data) return CURLE_OK; } -static CURLMcode state_performing(struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) +static CURLMcode multistate_performing(struct Curl_easy *data, + bool *stream_errorp, + CURLcode *resultp) { char *newurl = NULL; bool retry = FALSE; @@ -2139,9 +2160,9 @@ static CURLMcode state_performing(struct Curl_easy *data, return mresult; } -static CURLMcode state_do(struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) +static CURLMcode multistate_do(struct Curl_easy *data, + bool *stream_errorp, + CURLcode *resultp) { CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; @@ -2176,8 +2197,6 @@ static CURLMcode state_do(struct Curl_easy *data, /* Perform the protocol's DO action */ result = multi_do(data, &dophase_done); - /* When multi_do() returns failure, data->conn might be NULL! */ - if(!result) { if(!dophase_done) { #ifndef CURL_DISABLE_FTP @@ -2276,8 +2295,8 @@ static CURLMcode state_do(struct Curl_easy *data, return mresult; } -static CURLMcode state_ratelimiting(struct Curl_easy *data, - CURLcode *resultp) +static CURLMcode multistate_ratelimiting(struct Curl_easy *data, + CURLcode *resultp) { CURLcode result = CURLE_OK; CURLMcode mresult = CURLM_OK; @@ -2301,9 +2320,9 @@ static CURLMcode state_ratelimiting(struct Curl_easy *data, return mresult; } -static CURLMcode state_connect(struct Curl_multi *multi, - struct Curl_easy *data, - CURLcode *resultp) +static CURLMcode multistate_connect(struct Curl_multi *multi, + struct Curl_easy *data, + CURLcode *resultp) { /* Connect. We want to get a connection identifier filled in. This state can be entered from SETUP and from PENDING. */ @@ -2444,21 +2463,263 @@ static void handle_completed(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); Curl_uint32_bset_remove(&multi->pending, data->mid); Curl_uint32_bset_add(&multi->msgsent, data->mid); + if(data->state.really_alive) { + data->state.really_alive = FALSE; + --multi->xfers_really_alive; + if(!multi->xfers_really_alive) + (void)multi_assess_wakeup(multi); + } --multi->xfers_alive; if(!multi->xfers_alive) multi_assess_wakeup(multi); } -static CURLMcode multi_runsingle(struct Curl_multi *multi, - struct Curl_easy *data, - struct Curl_sigpipe_ctx *sigpipe_ctx) +static CURLMcode multistate_init(struct Curl_easy *data, CURLcode *result) +{ + if(!data->state.really_alive) { + data->state.really_alive = TRUE; + ++data->multi->xfers_really_alive; + if(data->multi->xfers_really_alive == 1) { + CURLMcode mresult = multi_assess_wakeup(data->multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; + } + } + } + + *result = Curl_pretransfer(data); + if(*result) + return CURLM_OK; + + /* after init, go SETUP */ + multistate(data, MSTATE_SETUP); + Curl_pgrsTime(data, TIMER_STARTOP); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_setup(struct Curl_easy *data) +{ + Curl_pgrsTime(data, TIMER_STARTSINGLE); + if(data->set.timeout) + Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); + if(data->set.connecttimeout) + /* Since a connection might go to pending and back to CONNECT several + times before it actually takes off, we need to set the timeout once + in SETUP before we enter CONNECT the first time. */ + Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); + + multistate(data, MSTATE_CONNECT); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_connecting(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) { bool connected; + + if(!data->conn) { + DEBUGASSERT(0); + *result = CURLE_FAILED_INIT; + return CURLM_OK; + } + if(!Curl_xfer_recv_is_paused(data)) { + *result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); + if(connected && !*result) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); + } + multistate(data, MSTATE_PROTOCONNECT); + return CURLM_CALL_MULTI_PERFORM; + } + else if(*result) { + /* failure detected */ + CURL_TRC_M(data, "connect failed -> %d", (int)*result); + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + return CURLM_OK; + } + } + return CURLM_OK; +} + +static CURLMcode multistate_protoconnect(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ bool protocol_connected = FALSE; + + if(!*result && data->conn->bits.reuse) { + /* ftp seems to hang when protoconnect on reused connection since we + * handle PROTOCONNECT in general inside the filters, it seems wrong to + * restart this on a reused connection. + */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + if(!*result) + *result = protocol_connect(data, &protocol_connected); + if(!*result && !protocol_connected) { + /* switch to waiting state */ + multistate(data, MSTATE_PROTOCONNECTING); + return CURLM_CALL_MULTI_PERFORM; + } + else if(!*result) { + /* protocol connect has completed, go WAITDO or DO */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + return CURLM_OK; +} + +static CURLMcode multistate_protoconnecting(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ + bool protocol_connected = FALSE; + + /* protocol-specific connect phase */ + *result = protocol_connecting(data, &protocol_connected); + if(!*result && protocol_connected) { + /* after the connect has completed, go WAITDO or DO */ + multistate(data, MSTATE_DO); + return CURLM_CALL_MULTI_PERFORM; + } + else if(*result) { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, TRUE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_doing(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ bool dophase_done = FALSE; + + /* we continue DOING until the DO phase is complete */ + DEBUGASSERT(data->conn); + *result = protocol_doing(data, &dophase_done); + if(!*result) { + if(dophase_done) { + /* after DO, go DO_DONE or DO_MORE */ + multistate(data, data->conn->bits.do_more ? + MSTATE_DOING_MORE : MSTATE_DID); + return CURLM_CALL_MULTI_PERFORM; + } /* dophase_done */ + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, FALSE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_doing_more(struct Curl_easy *data, + bool *stream_error, + CURLcode *result) +{ + domore control; + + /* + * When we are connected, DOING MORE and then go DID + */ + DEBUGASSERT(data->conn); + *result = multi_do_more(data, &control); + + if(!*result) { + if(control != DOMORE_INCOMPLETE) { + /* if DONE, advance to DO_DONE + if GOBACK, go back to DOING */ + multistate(data, control == DOMORE_DONE ? MSTATE_DID : MSTATE_DOING); + return CURLM_CALL_MULTI_PERFORM; + } + /* else + stay in DO_MORE */ + } + else { + /* failure detected */ + multi_posttransfer(data); + multi_done(data, *result, FALSE); + *stream_error = TRUE; + } + return CURLM_OK; +} + +static CURLMcode multistate_did(struct Curl_multi *multi, + struct Curl_easy *data) +{ + DEBUGASSERT(data->conn); + if(data->conn->bits.multiplex) + /* Check if we can move pending requests to send pipe */ + 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 */ + if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) || + CONN_SOCK_IDX_VALID(data->conn->send_idx)) + multistate(data, MSTATE_PERFORMING); + else { +#ifndef CURL_DISABLE_FTP + if(data->state.wildcardmatch && + ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) { + data->wildcard->state = CURLWC_DONE; + } +#endif + multistate(data, MSTATE_DONE); + } + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multistate_done(struct Curl_easy *data, CURLcode *presult) +{ + if(data->conn) { + CURLcode result; + + /* post-transfer command */ + result = multi_done(data, *presult, FALSE); + + /* allow a previously set error code take precedence */ + if(!(*presult)) + *presult = result; + } + +#ifndef CURL_DISABLE_FTP + if(data->state.wildcardmatch) { + if(data->wildcard->state != CURLWC_DONE) { + /* if a wildcard is set and we are not ending -> lets start again + with MSTATE_INIT */ + multistate(data, MSTATE_INIT); + return CURLM_CALL_MULTI_PERFORM; + } + } +#endif + /* after we have DONE what we are supposed to do, go COMPLETED, and + it does not matter what the multi_done() returned! */ + multistate(data, MSTATE_COMPLETED); + return CURLM_CALL_MULTI_PERFORM; +} + +static CURLMcode multi_runsingle(struct Curl_multi *multi, + struct Curl_easy *data, + struct Curl_sigpipe_ctx *sigpipe_ctx) +{ CURLMcode mresult; CURLcode result = CURLE_OK; - int control; if(!GOOD_EASY_HANDLE(data)) return CURLM_BAD_EASY_HANDLE; @@ -2479,6 +2740,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, Curl_uint32_bset_remove(&multi->dirty, data->mid); if(data == multi->admin) { +#ifdef ENABLE_WAKEUP + /* Consume any pending wakeup signals before processing. + * This is necessary for event based processing. See #21547 */ + (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); +#endif #ifdef USE_RESOLV_THREADED Curl_async_thrdd_multi_process(multi); #endif @@ -2517,217 +2783,63 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, case MSTATE_INIT: /* Transitional state. init this transfer. A handle never comes back to this state. */ - result = Curl_pretransfer(data); - if(result) - break; - - /* after init, go SETUP */ - multistate(data, MSTATE_SETUP); - Curl_pgrsTime(data, TIMER_STARTOP); - FALLTHROUGH(); + mresult = multistate_init(data, &result); + break; case MSTATE_SETUP: /* Transitional state. Setup things for a new transfer. The handle can come back to this state on a redirect. */ - Curl_pgrsTime(data, TIMER_STARTSINGLE); - if(data->set.timeout) - Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); - if(data->set.connecttimeout) - /* Since a connection might go to pending and back to CONNECT several - times before it actually takes off, we need to set the timeout once - in SETUP before we enter CONNECT the first time. */ - Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); - - multistate(data, MSTATE_CONNECT); - FALLTHROUGH(); + mresult = multistate_setup(data); + break; case MSTATE_CONNECT: - mresult = state_connect(multi, data, &result); + mresult = multistate_connect(multi, data, &result); break; case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ - 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 && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); - } - 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; - break; - } - } + mresult = multistate_connecting(data, &stream_error, &result); break; case MSTATE_PROTOCONNECT: - if(!result && data->conn->bits.reuse) { - /* ftp seems to hang when protoconnect on reused connection since we - * handle PROTOCONNECT in general inside the filers, it seems wrong to - * restart this on a reused connection. - */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - break; - } - if(!result) - result = protocol_connect(data, &protocol_connected); - if(!result && !protocol_connected) { - /* switch to waiting state */ - multistate(data, MSTATE_PROTOCONNECTING); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else if(!result) { - /* protocol connect has completed, go WAITDO or DO */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - } + mresult = multistate_protoconnect(data, &stream_error, &result); break; case MSTATE_PROTOCONNECTING: /* protocol-specific connect phase */ - result = protocol_connecting(data, &protocol_connected); - if(!result && protocol_connected) { - /* after the connect has completed, go WAITDO or DO */ - multistate(data, MSTATE_DO); - mresult = CURLM_CALL_MULTI_PERFORM; - } - else if(result) { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, TRUE); - stream_error = TRUE; - } + mresult = multistate_protoconnecting(data, &stream_error, &result); break; case MSTATE_DO: - mresult = state_do(data, &stream_error, &result); + mresult = multistate_do(data, &stream_error, &result); break; case MSTATE_DOING: /* we continue DOING until the DO phase is complete */ - DEBUGASSERT(data->conn); - result = protocol_doing(data, &dophase_done); - if(!result) { - if(dophase_done) { - /* after DO, go DO_DONE or DO_MORE */ - multistate(data, data->conn->bits.do_more ? - MSTATE_DOING_MORE : MSTATE_DID); - mresult = CURLM_CALL_MULTI_PERFORM; - } /* dophase_done */ - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - } + mresult = multistate_doing(data, &stream_error, &result); break; case MSTATE_DOING_MORE: /* * When we are connected, DOING MORE and then go DID */ - DEBUGASSERT(data->conn); - result = multi_do_more(data, &control); - - if(!result) { - if(control) { - /* if positive, advance to DO_DONE - if negative, go back to DOING */ - multistate(data, control == 1 ? MSTATE_DID : MSTATE_DOING); - mresult = CURLM_CALL_MULTI_PERFORM; - } - /* else - stay in DO_MORE */ - } - else { - /* failure detected */ - multi_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - } + mresult = multistate_doing_more(data, &stream_error, &result); break; case MSTATE_DID: - DEBUGASSERT(data->conn); - if(data->conn->bits.multiplex) - /* Check if we can move pending requests to send pipe */ - 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 */ - if(CONN_SOCK_IDX_VALID(data->conn->recv_idx) || - CONN_SOCK_IDX_VALID(data->conn->send_idx)) - multistate(data, MSTATE_PERFORMING); - else { -#ifndef CURL_DISABLE_FTP - if(data->state.wildcardmatch && - ((data->conn->scheme->flags & PROTOPT_WILDCARD) == 0)) { - data->wildcard->state = CURLWC_DONE; - } -#endif - multistate(data, MSTATE_DONE); - } - mresult = CURLM_CALL_MULTI_PERFORM; + mresult = multistate_did(multi, data); break; case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */ - mresult = state_ratelimiting(data, &result); + mresult = multistate_ratelimiting(data, &result); break; case MSTATE_PERFORMING: - mresult = state_performing(data, &stream_error, &result); + mresult = multistate_performing(data, &stream_error, &result); break; case MSTATE_DONE: - /* this state is highly transient, so run another loop after this */ - mresult = CURLM_CALL_MULTI_PERFORM; - - if(data->conn) { - CURLcode res; - - /* post-transfer command */ - res = multi_done(data, result, FALSE); - - /* allow a previously set error code take precedence */ - if(!result) - result = res; - } - -#ifndef CURL_DISABLE_FTP - if(data->state.wildcardmatch) { - if(data->wildcard->state != CURLWC_DONE) { - /* if a wildcard is set and we are not ending -> lets start again - with MSTATE_INIT */ - multistate(data, MSTATE_INIT); - break; - } - } -#endif - /* after we have DONE what we are supposed to do, go COMPLETED, and - it does not matter what the multi_done() returned! */ - multistate(data, MSTATE_COMPLETED); + mresult = multistate_done(data, &result); break; case MSTATE_COMPLETED: @@ -3897,8 +4009,7 @@ 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 */ - curlx_free(data->multi->xfer_buf); - data->multi->xfer_buf = NULL; + curlx_safefree(data->multi->xfer_buf); data->multi->xfer_buf_len = 0; } @@ -3950,8 +4061,7 @@ 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 */ - curlx_free(data->multi->xfer_ulbuf); - data->multi->xfer_ulbuf = NULL; + curlx_safefree(data->multi->xfer_ulbuf); data->multi->xfer_ulbuf_len = 0; } @@ -3985,11 +4095,12 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, size_t blen, char **pbuf) { DEBUGASSERT(data); - DEBUGASSERT(data->multi); *pbuf = NULL; if(!data->multi) { - failf(data, "transfer has no multi handle"); - return CURLE_FAILED_INIT; + /* When a SHARE gets destroyed and has a connection pool, we get + * call with share->admin which does not have a multi handle. */ + *pbuf = curlx_malloc(blen); + return *pbuf ? CURLE_OK : CURLE_OUT_OF_MEMORY; } if(data->multi->xfer_sockbuf_borrowed) { failf(data, "attempt to borrow xfer_sockbuf when already borrowed"); @@ -3998,8 +4109,7 @@ 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 */ - curlx_free(data->multi->xfer_sockbuf); - data->multi->xfer_sockbuf = NULL; + curlx_safefree(data->multi->xfer_sockbuf); data->multi->xfer_sockbuf_len = 0; } @@ -4019,11 +4129,16 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf) { - (void)buf; DEBUGASSERT(data); - DEBUGASSERT(data->multi); - DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf); - data->multi->xfer_sockbuf_borrowed = FALSE; + if(!data->multi) { + /* When a SHARE gets destroyed and has a connection pool, we get + * call with share->admin which does not have a multi handle. */ + curlx_free(buf); + } + else { + DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf); + data->multi->xfer_sockbuf_borrowed = FALSE; + } } static void multi_xfer_bufs_free(struct Curl_multi *multi) @@ -4054,6 +4169,9 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, unsigned int Curl_multi_xfers_running(struct Curl_multi *multi) { + DEBUGASSERT(multi); + if(!multi) + return 0; return multi->xfers_alive; } diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 937e7ce48d32..0da5b0e9044b 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -40,6 +40,10 @@ static void mev_in_callback(struct Curl_multi *multi, bool value) multi->in_callback = value; } +#ifdef DEBUGBUILD +#define SH_ENTRY_MAGIC 0x570091d +#endif + /* Information about a socket for which we inform the libcurl application * what to supervise (CURL_POLL_IN/CURL_POLL_OUT/CURL_POLL_REMOVE) */ @@ -51,6 +55,9 @@ struct mev_sh_entry { * libcurl application to watch out for */ unsigned int readers; /* this many transfers want to read */ unsigned int writers; /* this many transfers want to write */ +#ifdef DEBUGBUILD + unsigned int magic; +#endif BIT(announced); /* this socket has been passed to the socket callback at least once */ }; @@ -75,6 +82,9 @@ static void mev_sh_entry_dtor(void *freethis) { struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis; Curl_uint32_spbset_destroy(&entry->xfers); +#ifdef DEBUGBUILD + entry->magic = 0; +#endif curlx_free(entry); } @@ -113,7 +123,9 @@ static struct mev_sh_entry *mev_sh_entry_add(struct Curl_hash *sh, mev_sh_entry_dtor(check); return NULL; /* major failure */ } - +#ifdef DEBUGBUILD + check->magic = SH_ENTRY_MAGIC; +#endif return check; /* things are good in sockhash land */ } @@ -223,6 +235,7 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, /* we should only be called when the callback exists */ DEBUGASSERT(multi->socket_cb); + DEBUGASSERT(entry->magic == SH_ENTRY_MAGIC); if(!multi->socket_cb) return CURLM_OK; @@ -272,12 +285,18 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, entry->user_data); mev_in_callback(multi, FALSE); - entry->announced = TRUE; if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; } - entry->action = (unsigned int)comboaction; + /* curl_easy_pause() is documented as callable from any callback; it + * re-enters mev_assess() which may free this 'entry'. Re-fetch. */ + entry = mev_sh_entry_get(&multi->ev.sh_entries, s); + if(entry) { + DEBUGASSERT(entry->magic == SH_ENTRY_MAGIC); + entry->announced = TRUE; + entry->action = (unsigned int)comboaction; + } return CURLM_OK; } @@ -486,9 +505,9 @@ static CURLMcode mev_assess(struct Curl_multi *multi, Curl_pollset_init(&ps); if(conn) { - CURLcode r = Curl_conn_adjust_pollset(data, conn, &ps); - if(r) { - mresult = (r == CURLE_OUT_OF_MEMORY) ? + CURLcode result = Curl_conn_adjust_pollset(data, conn, &ps); + if(result) { + mresult = (result == CURLE_OUT_OF_MEMORY) ? CURLM_OUT_OF_MEMORY : CURLM_INTERNAL_ERROR; goto out; } diff --git a/lib/multihandle.h b/lib/multihandle.h index c5cdfbe82e42..19dd2ffcdf16 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -85,6 +85,8 @@ struct Curl_multi { unsigned int xfers_alive; /* amount of added transfers that have not yet reached COMPLETE state */ + unsigned int xfers_really_alive; /* amount of added transfers that have + passed INIT state but are not COMPLETE yet */ curl_off_t xfers_total_ever; /* total of added transfers, ever. */ struct uint32_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ diff --git a/lib/netrc.c b/lib/netrc.c index 72d8feee7d94..7c8e1fb2ab3f 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -36,88 +36,154 @@ #endif #include "netrc.h" +#include "urldata.h" +#include "creds.h" +#include "curl_trc.h" #include "strcase.h" #include "curl_get_line.h" #include "curlx/fopen.h" #include "curlx/strparse.h" -/* Get user and password from .netrc when given a machine name */ -enum host_lookup_state { - NOTHING, - HOSTFOUND, /* the 'machine' keyword was found */ - HOSTVALID, /* this is "our" machine! */ - MACDEF -}; - -enum found_state { - NONE, - LOGIN, - PASSWORD -}; - -#define FOUND_LOGIN 1 -#define FOUND_PASSWORD 2 +/* .netrc is not really a standard. The GNU definition can be found here: + * https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html + * This gives grammar like: + * + * LITERAL := \S+ | QUOTED + * QUOTED := "(\\[rnt\]|[^"])*" + * ANYTHING := . + * EMPTY_LINE := \r*\n\r*\n + * MACHINE := machine # case-insensitive + * LOGIN := login # case-insensitive + * PASSWD := password # case-insensitive + * ACCOUNT := account # case-insensitive + * MACDEF := macdef # case-insensitive + * DEFAULT := default # case-insensitive + * + * MACRO := MACDEF ANYTHING* EMPTY_LINE + * JUNK := LITERAL + * LKEY := ( LOGIN | PASSWD | ACCOUNT ) LITERAL + * MENTRY := MACHINE LITERAL LKEY* + * DENTRY := DEFAULT LKEY* + * NETRC := (MENTRY | DENTRY | MACRO | JUNK )* EOF + * + * Tokens are separated by whitespace or newlines. which have otherwise + * no special meaning, apart from the empty line ending a MACRO. + * + * Parsing is not strict, unmatched LITERALs are ignored + */ #define MAX_NETRC_LINE 16384 #define MAX_NETRC_FILE (128 * 1024) #define MAX_NETRC_TOKEN 4096 +#define NETRC_DEBUG 0 + /* convert a dynbuf call CURLcode error to a NETRCcode error */ -#define curl2netrc(result) \ - (((result) == CURLE_OUT_OF_MEMORY) ? \ - NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR) +#define curl2netrc(r) \ + ((!(r)) ? NETRC_OK : (((r) == CURLE_OUT_OF_MEMORY) ? \ + NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR)) + +typedef enum { + NETRC_TOK_EOF, + NETRC_TOK_LITERAL, + NETRC_TOK_MACHINE, + NETRC_TOK_DEFAULT, + NETRC_TOK_ACCOUNT, + NETRC_TOK_LOGIN, + NETRC_TOK_PASSWD, + NETRC_TOK_MACDEF, + NETRC_TOK_JUNK +} curl_netrc_token; + +struct netrc_lexer { + struct Curl_easy *data; + const char *content; + const char *pos; + struct dynbuf literal; + curl_netrc_token token; + bool pushed; +}; -static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) +#if NETRC_DEBUG +static const char *netrc_tokenstr(curl_netrc_token token) { - NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ - FILE *file = curlx_fopen(filename, FOPEN_READTEXT); + switch(token) { + case NETRC_TOK_EOF: + return "[EOF]"; + case NETRC_TOK_LITERAL: + return "[LITERAL]"; + case NETRC_TOK_MACHINE: + return "[MACHINE]"; + case NETRC_TOK_DEFAULT: + return "[DEFAULT]"; + case NETRC_TOK_ACCOUNT: + return "[ACCOUNT]"; + case NETRC_TOK_LOGIN: + return "[LOGIN]"; + case NETRC_TOK_PASSWD: + return "[PASSWORD]"; + case NETRC_TOK_MACDEF: + return "[MACDEF]"; + case NETRC_TOK_JUNK: + return "[JUNK]"; + default: + return "[???]"; + } +} +#endif - if(file) { - 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); +static void netrc_lexer_init(struct netrc_lexer *lexer, + struct Curl_easy *data, + const char *content) +{ + curlx_dyn_init(&lexer->literal, MAX_NETRC_TOKEN); + lexer->data = data; + lexer->content = lexer->pos = content; +} + +static void netrc_lexer_cleanup(struct netrc_lexer *lexer) +{ + lexer->content = lexer->pos = NULL; + lexer->data = NULL; + curlx_dyn_free(&lexer->literal); +} + +static void netrc_skip_blanks(struct netrc_lexer *lexer) +{ + const char *s = lexer->pos; + while(*s) { + curlx_str_passblanks(&s); + while(*s == '\r') + ++s; + if(*s == '\n') { + ++s; } - curlx_fclose(file); + else + break; } - return ret; + lexer->pos = s; } -/* 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; -}; +static void netrc_skip_to_empty_line(struct netrc_lexer *lexer) +{ + const char *s = lexer->pos; + while(*s) { + if(*s == '\r') + ++s; + else if(*s == '\n') { + ++s; + while(*s == '\r') + ++s; + if(*s == '\n') + goto out; + } + else + ++s; + } +out: + lexer->pos = s; +} /* * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t @@ -125,365 +191,379 @@ struct netrc_state { * * Returns NETRC_OK or error. */ -static NETRCcode netrc_quoted_token(const char **tok_endp, - struct dynbuf *token) +static NETRCcode netrc_lexer_quoted(struct netrc_lexer *lexer) { - 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; + const char *s = lexer->pos; + bool escape = FALSE; + CURLcode result; + + DEBUGASSERT(*s == '\"'); + ++s; /* pass the leading quote */ + while(*s) { + char c = *s; if(escape) { escape = FALSE; - switch(s) { + switch(c) { case 'n': - s = '\n'; + c = '\n'; break; case 'r': - s = '\r'; + c = '\r'; break; case 't': - s = '\t'; + c = '\t'; break; } } - else if(s == '\\') { + else if(c == '\\') { escape = TRUE; - tok_end++; + ++s; continue; } - else if(s == '\"') { - tok_end++; /* pass the ending quote */ + else if(c == '\"') { + ++s; /* pass the ending quote */ rc = NETRC_OK; - break; + goto out; } - result = curlx_dyn_addn(token, &s, 1); + result = curlx_dyn_addn(&lexer->literal, &c, 1); if(result) { - *tok_endp = tok_end; - return curl2netrc(result); + rc = curl2netrc(result); + goto out; } - tok_end++; + ++s; } - *tok_endp = tok_end; +out: + lexer->pos = s; 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) +static void netrc_lexer_push(struct netrc_lexer *lexer) { - 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; - } + lexer->pushed = TRUE; +} - if(!*tok || (*tok == '\n')) { - /* end of line */ - *lineend = TRUE; - *tokp = tok; - return NETRC_OK; +static NETRCcode netrc_lexer_next(struct netrc_lexer *lexer, + bool want_literal) +{ + const char *s = lexer->pos, *start; + NETRCcode rc = NETRC_OK; + size_t slen; + CURLcode result; + + if(lexer->pushed) { + lexer->pushed = FALSE; + goto out; } - tok_end = tok; - if(*tok == '\"') { - /* quoted string */ - NETRCcode ret = netrc_quoted_token(&tok_end, token); - if(ret) - return ret; - } - else { + curlx_dyn_reset(&lexer->literal); + netrc_skip_blanks(lexer); + s = lexer->pos; + + switch(*s) { + case 0: + lexer->token = NETRC_TOK_EOF; + break; + case '\"': + rc = netrc_lexer_quoted(lexer); + lexer->token = NETRC_TOK_LITERAL; + s = lexer->pos; + break; + default: /* unquoted token */ - size_t len = 0; - CURLcode result; - while(*tok_end > ' ') { - tok_end++; - len++; + start = s; + while(*s && !ISBLANK(*s) && !ISNEWLINE(*s)) + ++s; + slen = s - start; + if(!slen) { + rc = NETRC_SYNTAX_ERROR; + } + if(want_literal) { + lexer->token = NETRC_TOK_LITERAL; + result = curlx_dyn_addn(&lexer->literal, start, slen); + rc = curl2netrc(result); + } + else if((slen == 7) && curl_strnequal(start, "machine", slen)) { + lexer->token = NETRC_TOK_MACHINE; + } + else if((slen == 7) && curl_strnequal(start, "default", slen)) { + lexer->token = NETRC_TOK_DEFAULT; + } + else if((slen == 7) && curl_strnequal(start, "account", slen)) { + lexer->token = NETRC_TOK_ACCOUNT; + } + else if((slen == 5) && curl_strnequal(start, "login", slen)) { + lexer->token = NETRC_TOK_LOGIN; + } + else if((slen == 8) && curl_strnequal(start, "password", slen)) { + lexer->token = NETRC_TOK_PASSWD; } - if(!len) - return NETRC_SYNTAX_ERROR; - result = curlx_dyn_addn(token, tok, len); - if(result) - return curl2netrc(result); + else if((slen == 6) && curl_strnequal(start, "macdef", slen)) { + lexer->token = NETRC_TOK_MACDEF; + } + else { + lexer->token = NETRC_TOK_JUNK; + } + break; } - *tok_endp = tok_end; +out: +#if NETRC_DEBUG + CURL_TRC_M(lexer->data, "[NETRC] token %s '%s', rc=%d", + netrc_tokenstr(lexer->token), + curlx_dyn_ptr(&lexer->literal), rc); +#endif + lexer->pos = s; + return rc; +} - if(curlx_dyn_len(token)) - *tokp = curlx_dyn_ptr(token); - else - /* set it to blank to avoid NULL */ - *tokp = ""; +struct netrc_scanner { + struct netrc_lexer lexer; + const char *hostname; /* non-NULL, machine to scan for */ + const char *user; /* maybe NULL, login to scan for */ + char *login; + char *passwd; + struct Curl_creds *creds; + bool matches_host; + bool found; +}; - return NETRC_OK; +static void netrc_scan_reset(struct netrc_scanner *sc) +{ + curlx_safefree(sc->login); + curlx_safefree(sc->passwd); + sc->matches_host = FALSE; } -/* - * 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) +static void netrc_scan_init(struct netrc_scanner *sc, + struct Curl_easy *data, + const char *content, + const char *hostname, + const char *user) { - ns->keyword = NONE; - ns->found = 0; - ns->our_login = FALSE; - curlx_safefree(ns->password); - if(!ns->specific_login) - curlx_safefree(ns->login); + memset(sc, 0, sizeof(*sc)); + netrc_lexer_init(&sc->lexer, data, content); + sc->hostname = hostname; + sc->user = (user && user[0]) ? user : NULL; + netrc_scan_reset(sc); } -/* - * 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) +static void netrc_scan_cleanup(struct netrc_scanner *sc) { - 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; + netrc_scan_reset(sc); + sc->hostname = NULL; + sc->user = NULL; + Curl_creds_unlink(&sc->creds); + netrc_lexer_cleanup(&sc->lexer); } -/* - * 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) +static NETRCcode netrc_scan_literal(struct netrc_scanner *sc, + char **pdest) { - 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; + NETRCcode rc = netrc_lexer_next(&sc->lexer, TRUE); + if(!rc) { + if(sc->lexer.token == NETRC_TOK_LITERAL) { + if(pdest && sc->matches_host) { + curlx_free(*pdest); + *pdest = curlx_strdup(curlx_dyn_ptr(&sc->lexer.literal)); + if(!*pdest) + rc = NETRC_OUT_OF_MEMORY; + } } else - ns->state = NOTHING; - break; - case HOSTVALID: - return netrc_hostvalid(ns, tok); + netrc_lexer_push(&sc->lexer); } - return NETRC_OK; + return rc; } -/* - * 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) +static NETRCcode netrc_scan_end_entry(struct netrc_scanner *sc) { - 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; + NETRCcode rc = NETRC_OK; +#if NETRC_DEBUG + CURL_TRC_M(sc->lexer.data, + "[NETRC] entry matches_host=%d, login='%s', passwd='%s'", + sc->matches_host, sc->login, sc->passwd); +#endif + if(sc->matches_host) { + if(sc->login) { + if(sc->user) { + if(Curl_timestrcmp(sc->user, sc->login)) + goto out; + /* We look for a specific user, + * entry is only interesting with password */ + sc->found = !!sc->passwd; + } + else { + sc->found = TRUE; + } + } + else if(sc->passwd) { + /* found a passwd that applies to any user */ + sc->found = TRUE; + } + else { + /* entry has nothing interesting */ + } + if(sc->found) { +#if NETRC_DEBUG + CURL_TRC_M(sc->lexer.data, "[NETRC] entry match found"); +#endif + if(Curl_creds_create(sc->user ? sc->user : sc->login, sc->passwd, + NULL, NULL, NULL, CREDS_NETRC, &sc->creds)) + rc = 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; +out: + netrc_scan_reset(sc); + return rc; } -/* - * Returns zero on success. - */ -static NETRCcode parsenetrc(struct store_netrc *store, - const char *host, - char **loginp, - char **passwordp, - const char *netrcfile) +static NETRCcode netrc_scan(struct Curl_easy *data, + const char *content, + const char *hostname, + const char *user, + struct Curl_creds **pcreds) { - 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); - - if(!store->loaded) { - NETRCcode ret = file2memory(netrcfile, filebuf); - if(ret) - return ret; - store->loaded = TRUE; - } - - netrcbuffer = curlx_dyn_ptr(filebuf); - - while(!ns.done) { - const char *tok = netrcbuffer; - while(tok && !ns.done) { - const char *tok_end; - bool lineend; - NETRCcode ret; - - ret = netrc_get_token(&tok, &tok_end, &token, &ns.state, &lineend); - if(ret) { - ns.retcode = ret; - goto out; - } - if(lineend) + struct netrc_scanner sc; + NETRCcode rc = NETRC_OK; + + Curl_creds_unlink(pcreds); + netrc_scan_init(&sc, data, content, hostname, user); + + while(!rc && !sc.found) { + rc = netrc_lexer_next(&sc.lexer, FALSE); + if(!rc) { + /* Does this token end any previous entry? */ + switch(sc.lexer.token) { + case NETRC_TOK_EOF: + case NETRC_TOK_MACHINE: + case NETRC_TOK_DEFAULT: + case NETRC_TOK_MACDEF: + rc = netrc_scan_end_entry(&sc); + if(rc || sc.found) + goto out; break; + default: + break; + } - ret = netrc_handle_token(&ns, tok, host); - if(ret) { - ns.retcode = ret; + switch(sc.lexer.token) { + case NETRC_TOK_EOF: goto out; - } - /* tok_end cannot point to a null byte here since lines are always - newline terminated */ - DEBUGASSERT(*tok_end); - tok = ++tok_end; - } - if(!ns.done) { - const char *nl = NULL; - if(tok) - nl = strchr(tok, '\n'); - if(!nl) + case NETRC_TOK_MACHINE: + rc = netrc_lexer_next(&sc.lexer, TRUE); + if(!rc) { + if(sc.lexer.token == NETRC_TOK_LITERAL) { + sc.matches_host = curl_strequal( + sc.hostname, curlx_dyn_ptr(&sc.lexer.literal)); + } + else { + sc.matches_host = FALSE; + netrc_lexer_push(&sc.lexer); + } + } + break; + case NETRC_TOK_DEFAULT: + sc.matches_host = TRUE; + break; + case NETRC_TOK_ACCOUNT: + rc = netrc_scan_literal(&sc, NULL); /* ignore, not used */ + break; + case NETRC_TOK_LOGIN: + rc = netrc_scan_literal(&sc, &sc.login); break; - /* point to next line */ - netrcbuffer = &nl[1]; + case NETRC_TOK_PASSWD: + rc = netrc_scan_literal(&sc, &sc.passwd); + break; + case NETRC_TOK_MACDEF: + netrc_skip_to_empty_line(&sc.lexer); + break; + case NETRC_TOK_LITERAL: + case NETRC_TOK_JUNK: + default: + /* skip this */ + break; + } } - } /* while !done */ + } out: - curlx_dyn_free(&token); - return netrc_finalize(&ns, loginp, passwordp, store); + if(!rc) { + if(sc.creds) + Curl_creds_link(pcreds, sc.creds); + else + rc = NETRC_NO_MATCH; + } + netrc_scan_cleanup(&sc); + return rc; } -const char *Curl_netrc_strerror(NETRCcode ret) +static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf) { - switch(ret) { - default: - return ""; /* not a legit error */ - case NETRC_FILE_MISSING: - return "no such file"; - case NETRC_NO_MATCH: - return "no matching entry"; - case NETRC_OUT_OF_MEMORY: - return "out of memory"; - case NETRC_SYNTAX_ERROR: - return "syntax error"; + NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ + FILE *file = curlx_fopen(filename, FOPEN_READTEXT); + + curlx_dyn_reset(filebuf); + if(file) { + 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); } - /* never reached */ + return ret; +} + +static NETRCcode netrc_scan_file(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, + const char *netrcfile, + struct Curl_creds **pcreds) +{ + struct dynbuf *filebuf = &store->filebuf; + + if(!store->loaded || strcmp(netrcfile, store->filename)) { + NETRCcode ret; + store->loaded = FALSE; + ret = file2memory(netrcfile, filebuf); + if(ret) { + CURL_TRC_M(data, "[NETRC] could not load '%s'", netrcfile); + return ret; + } + curlx_free(store->filename); + store->filename = curlx_strdup(netrcfile); + if(!store->filename) { + curlx_dyn_reset(&store->filebuf); + return NETRC_OUT_OF_MEMORY; + } + store->loaded = TRUE; + } + + return netrc_scan(data, curlx_dyn_ptr(filebuf), hostname, user, pcreds); } /* @@ -492,16 +572,22 @@ const char *Curl_netrc_strerror(NETRCcode ret) * *loginp and *passwordp MUST be allocated if they are not NULL when passed * in. */ -NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - char **loginp, char **passwordp, - const char *netrcfile) +NETRCcode Curl_netrc_scan(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, + const char *netrcfile, + struct Curl_creds **pcreds) { NETRCcode retcode = NETRC_OK; - char *filealloc = NULL; + CURL_TRC_M(data, "[NETRC] scanning '%s' for host '%s' user '%s'", + netrcfile, hostname, user); + Curl_creds_unlink(pcreds); if(!netrcfile) { char *home = NULL; char *homea = NULL; + char *filealloc = NULL; #if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID) char pwbuf[1024]; #endif @@ -543,10 +629,12 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, filealloc = curl_maprintf("%s%s.netrc", home, DIR_CHAR); if(!filealloc) { curlx_free(homea); - return NETRC_OUT_OF_MEMORY; + retcode = NETRC_OUT_OF_MEMORY; + goto out; } } - retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + retcode = netrc_scan_file( + data, store, hostname, user, filealloc, pcreds); curlx_free(filealloc); #ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { @@ -556,14 +644,20 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, curlx_free(homea); return NETRC_OUT_OF_MEMORY; } - retcode = parsenetrc(store, host, loginp, passwordp, filealloc); + retcode = netrc_scan_file( + data, store, hostname, user, filealloc, pcreds); curlx_free(filealloc); } #endif curlx_free(homea); } else - retcode = parsenetrc(store, host, loginp, passwordp, netrcfile); + retcode = netrc_scan_file( + data, store, hostname, user, netrcfile, pcreds); + +out: + if(retcode) + Curl_creds_unlink(pcreds); return retcode; } @@ -571,10 +665,30 @@ void Curl_netrc_init(struct store_netrc *store) { curlx_dyn_init(&store->filebuf, MAX_NETRC_FILE); store->loaded = FALSE; + store->filename = NULL; } void Curl_netrc_cleanup(struct store_netrc *store) { curlx_dyn_free(&store->filebuf); + curlx_safefree(store->filename); store->loaded = FALSE; } -#endif + +const char *Curl_netrc_strerror(NETRCcode ret) +{ + switch(ret) { + default: + return ""; /* not a legit error */ + case NETRC_FILE_MISSING: + return "no such file"; + case NETRC_NO_MATCH: + return "no matching entry"; + case NETRC_OUT_OF_MEMORY: + return "out of memory"; + case NETRC_SYNTAX_ERROR: + return "syntax error"; + } + /* never reached */ +} + +#endif /* !CURL_DISABLE_NETRC */ diff --git a/lib/netrc.h b/lib/netrc.h index 90318c2bd645..6be9b8331621 100644 --- a/lib/netrc.h +++ b/lib/netrc.h @@ -29,6 +29,9 @@ #include "curlx/dynbuf.h" +struct Curl_easy; +struct Curl_creds; + struct store_netrc { struct dynbuf filebuf; char *filename; @@ -48,14 +51,14 @@ const char *Curl_netrc_strerror(NETRCcode ret); void Curl_netrc_init(struct store_netrc *store); void Curl_netrc_cleanup(struct store_netrc *store); -NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host, - char **loginp, char **passwordp, - 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. - */ +/* Scan a netrc file for credentials matching hostname + * and optional user. */ +NETRCcode Curl_netrc_scan(struct Curl_easy *data, + struct store_netrc *store, + const char *hostname, + const char *user, + const char *netrcfile, + struct Curl_creds **pcreds); #else /* disabled */ #define Curl_netrc_init(x) diff --git a/lib/noproxy.c b/lib/noproxy.c deleted file mode 100644 index 05c59a0f1aa6..000000000000 --- a/lib/noproxy.c +++ /dev/null @@ -1,266 +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" - -#ifndef CURL_DISABLE_PROXY - -#include "curlx/inet_pton.h" -#include "noproxy.h" -#include "curlx/strparse.h" - -#ifdef HAVE_NETINET_IN_H -#include -#endif - -#ifdef HAVE_ARPA_INET_H -#include -#endif - -/* - * cidr4_match() returns TRUE if the given IPv4 address is within the - * specified CIDR address range. - * - * @unittest 1614 - */ -UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits); -UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits) -{ - unsigned int address = 0; - unsigned int check = 0; - - if(bits > 32) - /* strange input */ - return FALSE; - - if(curlx_inet_pton(AF_INET, ipv4, &address) != 1) - return FALSE; - if(curlx_inet_pton(AF_INET, network, &check) != 1) - return FALSE; - - if(bits && (bits != 32)) { - unsigned int mask = 0xffffffff << (32 - bits); - unsigned int haddr = htonl(address); - unsigned int hcheck = htonl(check); -#if 0 - 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; - return TRUE; - } - return address == check; -} - -/* @unittest 1614 */ -UNITTEST bool cidr6_match(const char *ipv6, const char *network, - unsigned int bits); -UNITTEST bool cidr6_match(const char *ipv6, const char *network, - unsigned int bits) -{ -#ifdef USE_IPV6 - unsigned int bytes; - unsigned int rest; - unsigned char address[16]; - unsigned char check[16]; - - if(!bits) - bits = 128; - - bytes = bits / 8; - rest = bits & 0x07; - if((bytes > 16) || ((bytes == 16) && rest)) - return FALSE; - if(curlx_inet_pton(AF_INET6, ipv6, address) != 1) - return FALSE; - if(curlx_inet_pton(AF_INET6, network, check) != 1) - return FALSE; - if(bytes && memcmp(address, check, bytes)) - return FALSE; - if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) - return FALSE; - - return TRUE; -#else - (void)ipv6; - (void)network; - (void)bits; - return FALSE; -#endif -} - -enum nametype { - TYPE_HOST, - TYPE_IPV4, - 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 cidr6_match(name, checkip, bits); - else - return 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. - ****************************************************************/ -bool Curl_check_noproxy(const char *name, const char *no_proxy) -{ - /* - * 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. - */ - if(!name || name[0] == '\0') - return FALSE; - - /* no_proxy=domain1.dom,host.domain2.dom - * (a comma-separated list of hosts which should - * not be proxied, or an asterisk to override - * all proxy variables) - */ - 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 only an asterisk */ - - /* 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 { - /* ignore trailing dots in the hostname */ - if(name[namelen - 1] == '.') - namelen--; - } - - while(*p) { - const char *token; - size_t tokenlen = 0; - - /* pass blanks */ - curlx_str_passblanks(&p); - - token = p; - /* pass over the pattern */ - while(*p && !ISBLANK(*p) && (*p != ',')) { - p++; - tokenlen++; - } - - if(tokenlen) { - bool match = FALSE; - if(type == TYPE_HOST) - match = match_host(token, tokenlen, name, namelen); - else - match = match_ip(type, token, tokenlen, name); - - if(match) - return TRUE; - } - - /* pass blanks after pattern */ - curlx_str_passblanks(&p); - /* if not a comma, this ends the loop */ - if(*p != ',') - break; - /* pass any number of commas */ - while(*p == ',') - p++; - } /* while(*p) */ - } /* NO_PROXY was specified and it was not only an asterisk */ - - return FALSE; -} - -#endif /* CURL_DISABLE_PROXY */ diff --git a/lib/openldap.c b/lib/openldap.c index 48bf5b746d12..8ec6bb27cd84 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -345,9 +345,9 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) passwd.bv_val = NULL; passwd.bv_len = 0; - if(data->state.aptr.user) { - binddn = conn->user; - passwd.bv_val = conn->passwd; + if(conn->creds) { + binddn = Curl_creds_user(conn->creds); + passwd.bv_val = CURL_UNCONST(Curl_creds_passwd(conn->creds)); passwd.bv_len = strlen(passwd.bv_val); } @@ -355,7 +355,7 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate) NULL, NULL, &li->msgid); if(rc != LDAP_SUCCESS) return oldap_map_error(rc, - data->state.aptr.user ? + data->state.creds ? CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND); oldap_state(data, li, newstate); return CURLE_OK; @@ -499,7 +499,7 @@ static Sockbuf_IO ldapsb_tls = { static bool ssl_installed(struct connectdata *conn) { struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - return li && li->recv != NULL; + return li && li->recv; } static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate) @@ -617,8 +617,8 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done) hosturl = curl_maprintf("%s://%s:%d", conn->scheme->name, (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name, - conn->remote_port); + data->state.up.hostname : conn->origin->hostname, + conn->origin->port); if(!hosturl) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -900,7 +900,8 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) result = oldap_perform_bind(data, OLDAP_BIND); break; } - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) break; FALLTHROUGH(); @@ -911,7 +912,7 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done) else if(ssl_installed(conn)) { if(li->sasl.prefmech != SASL_AUTH_NONE) result = oldap_perform_mechs(data); - else if(data->state.aptr.user) + else if(data->state.creds) result = oldap_perform_bind(data, OLDAP_BIND); else { /* Version 3 supported: no bind required */ @@ -1177,7 +1178,7 @@ static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, binary = bv.bv_len > 7 && curl_strnequal(bv.bv_val + bv.bv_len - 7, ";binary", 7); - for(i = 0; bvals[i].bv_val != NULL; i++) { + for(i = 0; bvals[i].bv_val; i++) { bool binval = FALSE; result = client_write(data, STRCONST("\t"), bv.bv_val, bv.bv_len, diff --git a/lib/optiontable.pl b/lib/optiontable.pl index db0659cbb039..de6fb55151f2 100755 --- a/lib/optiontable.pl +++ b/lib/optiontable.pl @@ -39,7 +39,7 @@ HEAD ; -my $lastnum=0; +my $lastnum = 0; my %opt; my %type; @@ -47,7 +47,7 @@ my %alias; sub add { - my($optstr, $typestr, $num)=@_; + my($optstr, $typestr, $num) = @_; my $name; # remove all spaces from the type $typestr =~ s/ //g; @@ -59,7 +59,7 @@ sub add { } if($optstr =~ /^CURLOPT_(.*)/) { - $name=$1; + $name = $1; } $ext =~ s/CURLOPTTYPE_//; $ext =~ s/CBPOINT/CBPTR/; @@ -89,7 +89,7 @@ sub add { $fl .= $1; # the end - my @p=split(/, */, $fl); + my @p = split(/, */, $fl); add($p[0], $p[1], $p[2]); undef $fl; } @@ -106,14 +106,14 @@ sub add { } if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) { - my($opt, $type, $num)=($1,$2,$3); + my($opt, $type, $num) = ($1, $2, $3); add($opt, $type, $num); } # alias for an older option # old = new if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) { - my ($o, $n)=($1, $2); + my ($o, $n) = ($1, $2); # skip obsolete ones if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) { $o =~ s/^CURLOPT_//; diff --git a/lib/parsedate.c b/lib/parsedate.c index ce358bd465ab..b19423169af9 100644 --- a/lib/parsedate.c +++ b/lib/parsedate.c @@ -98,8 +98,12 @@ const char * const Curl_month[] = { #ifndef CURL_DISABLE_PARSEDATE +#if SIZEOF_TIME_T < 5 #define PARSEDATE_LATER 1 +#endif +#if SIZEOF_TIME_T < 5 || defined(HAVE_TIME_T_UNSIGNED) #define PARSEDATE_SOONER 2 +#endif static const char * const weekday[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" diff --git a/lib/peer.c b/lib/peer.c new file mode 100644 index 000000000000..44bcb52f2314 --- /dev/null +++ b/lib/peer.c @@ -0,0 +1,740 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 + * + ***************************************************************************/ +/* + * IDN conversions + */ +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#ifdef HAVE_IPHLPAPI_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#ifdef __VMS +#include +#include +#endif + +#ifdef HAVE_SYS_UN_H +#include +#endif + +#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 "curl_addrinfo.h" +#include "curl_trc.h" +#include "protocol.h" +#include "http_proxy.h" +#include "idn.h" +#include "curlx/strdup.h" +#include "curlx/strparse.h" +#include "peer.h" +#include "urldata.h" +#include "url.h" +#include "vtls/vtls.h" + +struct peer_parse { + const struct Curl_scheme *scheme; + struct Curl_str host_user; + struct Curl_str host; + struct Curl_str zoneid; + char *tmp_host_user; + char *tmp_host; + char *tmp_zoneid; + uint32_t scopeid; + uint16_t port; + bool ipv6; + bool unix_socket; + bool abstract_uds; +}; + +static void peer_parse_clear(struct peer_parse *pp) +{ + curlx_free(pp->tmp_host_user); + curlx_free(pp->tmp_host); + curlx_free(pp->tmp_zoneid); + memset(pp, 0, sizeof(*pp)); +} + +static CURLcode peer_create(struct peer_parse *pp, + struct Curl_peer **ppeer) +{ + struct Curl_peer *peer = NULL; + CURLcode result = CURLE_OK; + size_t zone_alen = 0, host_alen = 0; + + if(!pp || !pp->scheme) + return CURLE_FAILED_INIT; + if(!pp->host.len && !(pp->scheme->flags & PROTOPT_NONETWORK)) + return CURLE_FAILED_INIT; + + if((pp->host.str != pp->host_user.str) || + (pp->host.len != pp->host_user.len)) { + host_alen = pp->host.len + 1; + } + zone_alen = pp->zoneid.len ? (pp->zoneid.len + 1) : 0; + + /* null-terminator already part of struct */ + peer = curlx_calloc(1, sizeof(*peer) + + pp->host_user.len + host_alen + zone_alen); + if(!peer) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + peer->refcount = 1; + peer->scheme = pp->scheme; + peer->hostname = peer->user_hostname; + peer->port = pp->port; + peer->scopeid = pp->scopeid; + peer->ipv6 = pp->ipv6; + peer->unix_socket = pp->unix_socket; + peer->abstract_uds = pp->abstract_uds; + + if(pp->host_user.len) + memcpy(peer->user_hostname, pp->host_user.str, pp->host_user.len); + + if(host_alen) { + peer->hostname = peer->user_hostname + pp->host_user.len + 1; + memcpy(peer->hostname, pp->host.str, pp->host.len); + } + + if(zone_alen) { + peer->zoneid = peer->user_hostname + pp->host_user.len + 1 + host_alen; + memcpy(peer->zoneid, pp->zoneid.str, pp->zoneid.len); +#ifdef USE_IPV6 + /* Determine scope_id if not already provided */ + if(!peer->scopeid) { + const char *p = peer->zoneid; + curl_off_t scope; + if(!curlx_str_number(&p, &scope, UINT_MAX)) { + /* A plain number, use it directly as a scope id. */ + peer->scopeid = (uint32_t)scope; + } +#ifdef HAVE_IF_NAMETOINDEX + else { + /* Zone identifier is not numeric */ + unsigned int idx = 0; + idx = if_nametoindex(peer->zoneid); + if(idx) { + peer->scopeid = (uint32_t)idx; + } + else { + /* Do we want to return an error here? */ + } + } +#endif /* HAVE_IF_NAMETOINDEX */ + } +#endif /* USE_IPV6 */ + } + +out: + if(!result) + *ppeer = peer; + else + Curl_peer_unlink(&peer); + return result; +} + +static CURLcode peer_parse_host(struct Curl_easy *data, + struct peer_parse *pp, + bool scan_for_ipv6) +{ + if(!pp || !pp->host_user.str || !pp->host_user.len) + return CURLE_FAILED_INIT; + + if(pp->host_user.str[0] == '[') { + const char *s = pp->host_user.str + 1; + struct Curl_str tmp; + if(curlx_str_until(&s, &tmp, pp->host_user.len - 1, ']')) + return CURLE_URL_MALFORMAT; + + if(!Curl_looks_like_ipv6(tmp.str, tmp.len, TRUE, + &pp->host, &pp->zoneid)) { + failf(data, "Invalid IPv6 address format in '%.*s'", + (int)pp->host_user.len, pp->host_user.str); + return CURLE_URL_MALFORMAT; + } + pp->ipv6 = TRUE; + } + else { +#ifdef USE_IDN + if(!Curl_is_ASCII_str(&pp->host_user)) { + CURLcode result; + if(!pp->tmp_host_user) { + /* need a null-terminated string for IDN */ + pp->tmp_host_user = curlx_memdup0(pp->host_user.str, + pp->host_user.len); + if(!pp->tmp_host_user) + return CURLE_OUT_OF_MEMORY; + } + result = Curl_idn_decode(pp->tmp_host_user, &pp->tmp_host); + if(result) + return result; + pp->host.str = pp->tmp_host; + pp->host.len = strlen(pp->host.str); + } + else +#endif + if(scan_for_ipv6 && + Curl_looks_like_ipv6(pp->host_user.str, pp->host_user.len, TRUE, + &pp->host, &pp->zoneid)) { + if(pp->host_user.len < MAX_IPADR_LEN) { + char tmp[MAX_IPADR_LEN]; + memcpy(tmp, pp->host_user.str, pp->host_user.len); + tmp[pp->host_user.len] = 0; + pp->ipv6 = !Curl_is_ipv4addr(tmp); + } + else + pp->ipv6 = TRUE; + } + else + pp->host = pp->host_user; + } + return CURLE_OK; +} + +CURLcode Curl_peer_create(struct Curl_easy *data, + const struct Curl_scheme *scheme, + const char *hostname, + uint16_t port, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + pp.scheme = scheme; + pp.host_user.str = hostname; + pp.host_user.len = strlen(hostname); + pp.port = port; + + result = peer_parse_host(data, &pp, TRUE); + if(!result) + result = peer_create(&pp, ppeer); + + peer_parse_clear(&pp); + return result; +} + +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, + const char *path, + bool abstract_unix_socket, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + size_t pathlen = path ? strlen(path) : 0; + CURLcode result = CURLE_OK; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + if(!scheme) + return CURLE_FAILED_INIT; + if(!pathlen) + return CURLE_FAILED_INIT; + + pp.scheme = scheme; + pp.host_user.str = pp.host.str = path; + pp.host_user.len = pp.host.len = pathlen; + pp.unix_socket = TRUE; + pp.abstract_uds = abstract_unix_socket; + + result = peer_create(&pp, ppeer); + peer_parse_clear(&pp); + return result; +} +#endif /* USE_UNIX_SOCKETS */ + +void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src) +{ + if(*pdest != src) { + Curl_peer_unlink(pdest); + *pdest = src; + if(src) { + DEBUGASSERT(src->refcount < UINT32_MAX); + src->refcount++; + } + } +} + +void Curl_peer_unlink(struct Curl_peer **ppeer) +{ + if(*ppeer) { + struct Curl_peer *peer = *ppeer; + + DEBUGASSERT(peer->refcount); + *ppeer = NULL; + if(peer->refcount) + peer->refcount--; + if(!peer->refcount) { + curlx_free(peer); + } + } +} + +bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2) +{ + return (p1 == p2) || + (p1 && p2 && + (p1->scheme == p2->scheme) && + Curl_peer_same_destination(p1, p2)); +} + +static bool peer_same_hostname(struct Curl_peer *p1, struct Curl_peer *p2) +{ + /* UNIX domain socket paths must be compared case-sensitive, + * as many filesystem are like that. */ + return (p1->unix_socket == p2->unix_socket) && + (p1->abstract_uds == p2->abstract_uds) && + (p1->ipv6 == p2->ipv6) && + (p1->unix_socket ? + !strcmp(p1->hostname, p2->hostname) : + curl_strequal(p1->hostname, p2->hostname)); +} + +bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2) +{ + return (p1 == p2) || + (p1 && p2 && + (p1->port == p2->port) && + peer_same_hostname(p1, p2) && + (p1->scopeid == p2->scopeid) && + (p1->scopeid || curl_strequal(p1->zoneid, p2->zoneid))); +} + +CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, + uint16_t port_override, + uint32_t scopeid_override, + struct urlpieces *up, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + char *zoneid = NULL; + CURLUcode uc; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + + curlx_safefree(up->scheme); + uc = curl_url_get(uh, CURLUPART_SCHEME, &up->scheme, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + pp.scheme = Curl_get_scheme(up->scheme); + if(!pp.scheme) { + failf(data, "Protocol \"%s\" not supported%s", up->scheme, + data->state.this_is_a_follow ? " (in redirect)" : ""); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + + curlx_safefree(up->hostname); + uc = curl_url_get(uh, CURLUPART_HOST, &up->hostname, 0); + if(uc) { + if((uc == CURLUE_NO_HOST) && (pp.scheme->flags & PROTOPT_NONETWORK)) + ; /* acceptable */ + else { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else if(strlen(up->hostname) > MAX_URL_LEN) { + failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); + result = CURLE_URL_MALFORMAT; + goto out; + } + + pp.host_user.str = up->hostname ? up->hostname : ""; + pp.host_user.len = strlen(pp.host_user.str); + if(pp.host_user.len) { + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + } + else + pp.host = pp.host_user; + + curlx_safefree(up->port); + if(port_override) { + /* if set, we use this instead of the port possibly given in the URL */ + char portbuf[16]; + curl_msnprintf(portbuf, sizeof(portbuf), "%d", port_override); + uc = curl_url_set(uh, CURLUPART_PORT, portbuf, 0); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + else + pp.port = port_override; + } + else { + uc = curl_url_get(uh, CURLUPART_PORT, &up->port, CURLU_DEFAULT_PORT); + if(uc) { + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + else if(!(pp.scheme->flags & PROTOPT_NONETWORK)) { + result = CURLE_URL_MALFORMAT; + goto out; + } + /* no port ok when not a network scheme */ + } + else { + const char *p = up->port; + curl_off_t offt; + if(curlx_str_number(&p, &offt, 0xffff)) + return CURLE_URL_MALFORMAT; + pp.port = (uint16_t)offt; + } + } + + if(scopeid_override) + /* Override any scope id from an URL zone. */ + pp.scopeid = scopeid_override; + else { + if(curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0) == + CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(zoneid) { + pp.zoneid.str = zoneid; + pp.zoneid.len = strlen(zoneid); + } + } + + result = peer_create(&pp, ppeer); + if(result) + failf(data, "Error %d creating peer for %s:%u", + (int)result, pp.host_user.str, pp.port); + +out: + peer_parse_clear(&pp); + curlx_free(zoneid); + return result; +} + +/* Parse a "host:port" string to connect to into a peer. + * IPv6 addresses might appear in brackets or without them. */ +CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, + const struct Curl_peer *dest, + const char *connect_to, + struct Curl_peer **ppeer) +{ + struct peer_parse pp; + const char *portstr = NULL; + CURLcode result; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + if(!connect_to || !*connect_to) + return CURLE_FAILED_INIT; + + pp.scheme = dest->scheme; + + /* detect and extract RFC6874-style IPv6-addresses */ + if(connect_to[0] == '[') { + const char *s = strchr(connect_to + 1, ']'); + if(!s) { + failf(data, "Invalid IPv6 address format in '%s'", connect_to); + result = CURLE_SETOPT_OPTION_SYNTAX; + goto out; + } + portstr = strchr(s, ':'); + pp.host_user.str = connect_to; + pp.host_user.len = s - pp.host_user.str + 1; + pp.ipv6 = TRUE; + } + else { + portstr = strchr(connect_to, ':'); + pp.host_user.str = connect_to; + pp.host_user.len = portstr ? + (size_t)(portstr - connect_to) : strlen(connect_to); + } + + if(!pp.host_user.len) { /* no hostname found, only port switch */ + pp.host_user.str = dest->user_hostname; + pp.host_user.len = strlen(dest->user_hostname); + } + + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + + if(portstr && portstr[1]) { + const char *p = portstr + 1; + curl_off_t portparse; + if(curlx_str_number(&p, &portparse, 0xffff)) { + failf(data, "No valid port number in '%s'", connect_to); + result = CURLE_SETOPT_OPTION_SYNTAX; + goto out; + } + pp.port = (uint16_t)portparse; /* we know it will fit */ + } + else + pp.port = dest->port; + +#ifndef USE_IPV6 + if(pp.ipv6) { + failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); + result = CURLE_NOT_BUILT_IN; + goto out; + } +#endif + + result = peer_create(&pp, ppeer); + CURL_TRC_M(data, "connect-to peer_create2 -> %d", (int)result); + +out: + CURL_TRC_M(data, "parse connect_to peer: %s -> %d", connect_to, (int)result); + peer_parse_clear(&pp); + return result; +} + +#ifndef CURL_DISABLE_PROXY + +#ifdef USE_UNIX_SOCKETS +#define UNIX_SOCKET_PREFIX "localhost" +#endif + +CURLcode Curl_scheme_to_proxytype(struct Curl_easy *data, + const char *scheme, + uint8_t *proxytype, const char *url) +{ + if(!scheme) + return CURLE_OK; + + if(curl_strequal("https", scheme)) { + if(*proxytype != CURLPROXY_HTTPS2 && *proxytype != CURLPROXY_HTTPS3) + *proxytype = CURLPROXY_HTTPS; + } + else if(curl_strequal("socks5h", scheme)) + *proxytype = CURLPROXY_SOCKS5_HOSTNAME; + else if(curl_strequal("socks5", scheme)) + *proxytype = CURLPROXY_SOCKS5; + else if(curl_strequal("socks4a", scheme)) + *proxytype = CURLPROXY_SOCKS4A; + else if(curl_strequal("socks4", scheme) || curl_strequal("socks", scheme)) + *proxytype = CURLPROXY_SOCKS4; + else if(curl_strequal("http", scheme)) { + if(*proxytype != CURLPROXY_HTTP_1_0) + *proxytype = CURLPROXY_HTTP; + } + else { + /* Any other xxx:// reject! */ + failf(data, "Unsupported proxy scheme for \'%s\'", url); + return CURLE_COULDNT_CONNECT; + } + return CURLE_OK; +} + +CURLcode Curl_peer_from_proxy_url(CURLU *uh, + struct Curl_easy *data, + const char *url, + uint8_t proxytype, + struct Curl_peer **ppeer, + uint8_t *pproxytype) +{ + struct peer_parse pp; + char *scheme = NULL; + char *portptr = NULL; +#ifdef USE_UNIX_SOCKETS + bool is_socks = FALSE; +#endif + CURLUcode uc; + CURLcode result = CURLE_OK; + + Curl_peer_unlink(ppeer); + memset(&pp, 0, sizeof(pp)); + pp.port = CURL_DEFAULT_PROXY_PORT; + uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme, + CURLU_NON_SUPPORT_SCHEME | CURLU_NO_GUESS_SCHEME); + if(uc) { + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + /* URL came without scheme, the passed `proxytype` determines it */ + switch(proxytype) { + case CURLPROXY_HTTP: + case CURLPROXY_HTTP_1_0: + pp.scheme = &Curl_scheme_http; + break; + case CURLPROXY_HTTPS: + case CURLPROXY_HTTPS2: + case CURLPROXY_HTTPS3: + pp.scheme = &Curl_scheme_https; + break; + case CURLPROXY_SOCKS4: + pp.scheme = &Curl_scheme_socks4; + break; + case CURLPROXY_SOCKS4A: + pp.scheme = &Curl_scheme_socks4a; + break; + case CURLPROXY_SOCKS5: + pp.scheme = &Curl_scheme_socks5; + break; + case CURLPROXY_SOCKS5_HOSTNAME: + pp.scheme = &Curl_scheme_socks5h; + break; + default: + failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, url); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto out; + } + } + else { + pp.scheme = Curl_get_scheme(scheme); + result = Curl_scheme_to_proxytype(data, scheme, &proxytype, url); + if(result) + goto out; + } + DEBUGASSERT(pp.scheme); + + if(CURL_PROXY_IS_HTTPS(proxytype) && + !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { + failf(data, "Unsupported proxy \'%s\', libcurl is built without the " + "HTTPS-proxy support.", url); + result = CURLE_NOT_BUILT_IN; + goto out; + } + + switch(pp.scheme->family) { + case CURLPROTO_SOCKS: +#ifdef USE_UNIX_SOCKETS + is_socks = TRUE; +#endif + break; + case CURLPROTO_HTTP: + break; + default: + failf(data, "Unsupported proxy protocol for \'%s\'", url); + result = CURLE_COULDNT_CONNECT; + goto out; + } + + uc = curl_url_get(uh, CURLUPART_PORT, &portptr, CURLU_NO_DEFAULT_PORT); + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(portptr) { + curl_off_t num; + const char *p = portptr; + if(!curlx_str_number(&p, &num, UINT16_MAX)) + pp.port = (uint16_t)num; + /* Should we not error out when the port number is invalid? */ + curlx_free(portptr); + } + else { + /* No port in URL, take the set one or the scheme's default */ + if(data->set.proxyport) + pp.port = data->set.proxyport; + else + pp.port = pp.scheme->defport; + } + + /* now, clone the proxy hostname */ + uc = curl_url_get(uh, CURLUPART_HOST, &pp.tmp_host_user, CURLU_URLDECODE); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + pp.host_user.str = pp.tmp_host_user; + pp.host_user.len = strlen(pp.tmp_host_user); + +#ifdef USE_UNIX_SOCKETS + if(is_socks && curl_strequal(UNIX_SOCKET_PREFIX, pp.tmp_host_user)) { + uc = curl_url_get(uh, CURLUPART_PATH, &pp.tmp_host, CURLU_URLDECODE); + if(uc) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + /* path will be "/", if no path was found */ + if(strcmp("/", pp.tmp_host)) { + pp.host.str = pp.tmp_host; + pp.host.len = strlen(pp.tmp_host); + pp.unix_socket = TRUE; + } + else { + pp.host = pp.host_user; + } + } +#endif /* USE_UNIX_SOCKETS */ + + if(!pp.host.len) { + result = peer_parse_host(data, &pp, FALSE); + if(result) + goto out; + } + + uc = curl_url_get(uh, CURLUPART_ZONEID, &pp.tmp_zoneid, 0); + if(uc == CURLUE_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + if(pp.tmp_zoneid) { + pp.zoneid.str = pp.tmp_zoneid; + pp.zoneid.len = strlen(pp.tmp_zoneid); + } + + *pproxytype = proxytype; + result = peer_create(&pp, ppeer); + +out: + peer_parse_clear(&pp); + curlx_free(scheme); +#ifdef DEBUGBUILD + if(!result) + DEBUGASSERT(*ppeer); +#endif + return result; +} + +#endif /* !CURL_DISABLE_PROXY */ diff --git a/lib/peer.h b/lib/peer.h new file mode 100644 index 000000000000..1ceb230739b2 --- /dev/null +++ b/lib/peer.h @@ -0,0 +1,110 @@ +#ifndef HEADER_CURL_PEER_H +#define HEADER_CURL_PEER_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_scheme; +struct urlpieces; + +/* if peer hostname starts with this, the peer is a unix domain socket + * path, e.g. the remainder after 'localhost'. */ +#define CURL_PEER_UDS_PREFIX "localhost/" + +struct Curl_peer { + const struct Curl_scheme *scheme; /* url scheme */ + char *hostname; /* normalized hostname (IDN decoded when supported) */ + char *zoneid; /* NULL or ipv6 zone identifier */ + uint32_t refcount; /* created with 1, freed when dropping to 0 */ + uint32_t scopeid; /* != 0, ipv6 scope to use */ + uint16_t port; + BIT(unix_socket); /* hostname is a UDS path without the prefix */ + BIT(abstract_uds); /* only TRUE when `unix_socket` also TRUE */ + BIT(ipv6); /* hostname is an IPv6 address stripped of '[]' */ + char user_hostname[1]; /* hostname supplied by user/url */ +}; + +/* Create a new peer: + * - `peer->user_hostname` is the passed `hostname` + * - `peer->hostname` is the normalized `hostname` via + * + IDN conversion if it has non-ASCII characters + * + stripping of surrounding '[]' for URL formatted ipv6 addresses + * + the path alone in case of a unix domain socket, e.g. hostname + * starts with CURL_PEER_UDS_PREFIX and is longer + * Scans for IPv6 addresses even without surrounding '[]'. + * - `zoneid` ipv6 zone identifier or NULL + * - `scopeid` ipv6 scopeid of zoneid, when known. + */ +CURLcode Curl_peer_create(struct Curl_easy *data, + const struct Curl_scheme *scheme, + const char *hostname, + uint16_t port, + struct Curl_peer **ppeer); + +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme, + const char *path, + bool abstract_unix_socket, + struct Curl_peer **ppeer); +#endif + +/* Unlink any peer in `*pdest`, assign src, increase src + * refcount when not NULL. */ +void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src); + +/* Drop a reference, peer may be passed as NULL */ +void Curl_peer_unlink(struct Curl_peer **ppeer); + +/* TRUE if both peers are NULL or have completely same properties. */ +bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2); + +/* TRUE if both peers are NULL or have same properties except the scheme. */ +bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2); + +CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data, + uint16_t port_override, + uint32_t scopeid_override, + struct urlpieces *up, + struct Curl_peer **ppeer); + +CURLcode Curl_peer_from_connect_to(struct Curl_easy *data, + const struct Curl_peer *dest, + const char *connect_to, + struct Curl_peer **ppeer); + +#ifndef CURL_DISABLE_PROXY + +CURLcode Curl_scheme_to_proxytype(struct Curl_easy *data, + const char *scheme, + uint8_t *proxytype, + const char *url); + +CURLcode Curl_peer_from_proxy_url(CURLU *uh, + struct Curl_easy *data, + const char *url, + uint8_t proxytype, + struct Curl_peer **ppeer, + uint8_t *pproxytype); +#endif /* !CURL_DISABLE_PROXY */ + +#endif /* HEADER_CURL_PEER_H */ diff --git a/lib/pingpong.c b/lib/pingpong.c index 44e424418d88..b40d968b3f48 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -120,13 +120,13 @@ 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, const struct curltime *pnow) { - DEBUGASSERT(!pp->initialised); + DEBUGASSERT(!pp->initialized); pp->nread_resp = 0; 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); - pp->initialised = TRUE; + pp->initialized = TRUE; } /*********************************************************************** @@ -152,7 +152,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data, DEBUGASSERT(pp->sendleft == 0); DEBUGASSERT(pp->sendsize == 0); - DEBUGASSERT(pp->sendthis == NULL); + DEBUGASSERT(!pp->sendthis); if(!conn) /* cannot send without a connection! */ @@ -292,6 +292,13 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, the line is not really terminated until the LF comes */ size_t length = nl - line + 1; + if(memchr(line, 0, length)) { + /* The response line is passed on as a "header" below, so reject an + embedded nul the same way verify_header() does for HTTP. */ + failf(data, "Nul byte in server response line"); + return CURLE_WEIRD_SERVER_REPLY; + } + /* output debug output if that is requested */ Curl_debug(data, CURLINFO_HEADER_IN, line, length); @@ -389,7 +396,7 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data, CURLcode Curl_pp_disconnect(struct pingpong *pp) { - if(pp->initialised) { + if(pp->initialized) { curlx_dyn_free(&pp->sendbuf); curlx_dyn_free(&pp->recvbuf); memset(pp, 0, sizeof(*pp)); diff --git a/lib/pingpong.h b/lib/pingpong.h index 864f2c68350e..02f496123036 100644 --- a/lib/pingpong.h +++ b/lib/pingpong.h @@ -64,7 +64,7 @@ struct pingpong { CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn); bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn, const char *ptr, size_t len, int *code); - BIT(initialised); + BIT(initialized); BIT(pending_resp); /* set TRUE when a server response is pending or in progress, and is cleared once the last response is read */ diff --git a/lib/pop3.c b/lib/pop3.c index 317c04bbe363..d609b4c2b31b 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -231,7 +231,7 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn) */ static CURLcode pop3_parse_url_path(struct Curl_easy *data) { - /* The POP3 struct is already initialised in pop3_connect() */ + /* The POP3 struct is already initialized 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 */ @@ -485,7 +485,8 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, return CURLE_FAILED_INIT; if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler */ @@ -495,7 +496,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!pop3c->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { pop3c->ssldone = ssldone; /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */ @@ -527,7 +528,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!conn->creds) { pop3_state(data, POP3_STOP); return result; @@ -535,7 +536,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data, /* Send the USER command */ result = Curl_pp_sendf(data, &pop3c->pp, "USER %s", - conn->user ? conn->user : ""); + Curl_creds_user(conn->creds)); if(!result) pop3_state(data, POP3_USER); @@ -564,7 +565,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, /* Check we have a username and password to authenticate with and end the connect phase if we do not */ - if(!data->state.aptr.user) { + if(!data->state.creds) { pop3_state(data, POP3_STOP); return result; @@ -578,17 +579,18 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, Curl_MD5_update(ctxt, (const unsigned char *)pop3c->apoptimestamp, curlx_uztoui(strlen(pop3c->apoptimestamp))); - Curl_MD5_update(ctxt, (const unsigned char *)conn->passwd, - curlx_uztoui(strlen(conn->passwd))); + Curl_MD5_update(ctxt, (const unsigned char *)Curl_creds_passwd(conn->creds), + curlx_uztoui(strlen(Curl_creds_passwd(conn->creds)))); /* Finalise the digest */ Curl_MD5_final(ctxt, digest); - /* Convert the calculated 16 octet digest into a 32 byte hex string */ + /* Convert the calculated 16 octet digest into a 32-byte hex string */ for(i = 0; i < MD5_DIGEST_LEN; i++) curl_msnprintf(&secret[2 * i], 3, "%02x", digest[i]); - result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret); + result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", + Curl_creds_user(conn->creds), secret); if(!result) pop3_state(data, POP3_APOP); @@ -1038,7 +1040,8 @@ 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); + result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s", + Curl_creds_passwd(conn->creds)); if(!result) pop3_state(data, POP3_PASS); @@ -1437,7 +1440,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done) pop3c->preftype = POP3_TYPE_ANY; Curl_sasl_init(&pop3c->sasl, data, &saslpop3); - /* Initialise the pingpong layer */ + /* Initialize the pingpong layer */ Curl_pp_init(pp, Curl_pgrs_now(data)); /* Parse the URL options */ diff --git a/lib/progress.c b/lib/progress.c index 919c151e75e4..969b29a275ca 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -240,6 +240,30 @@ void Curl_pgrsSendPause(struct Curl_easy *data, bool enable) } } +#ifdef CURLVERBOSE +static const char * const pgrs_timer_names[] = { + "PGRS-NONE", + "PGRS-STARTOP", + "PGRS-STARTSINGLE", + "PGRS-POSTQUEUE", + "PGRS-NAMELOOKUP", + "PGRS-CONNECT", + "PGRS-APPCONNECT", + "PGRS-PRETRANSFER", + "PGRS-STARTTRANSFER", + "PGRS-POSTRANSFER", + "PGRS-STARTACCEPT", + "PGRS-REDIRECT", +}; + +static const char *pgrs_timer_name(timerid timer) +{ + if((size_t)timer < CURL_ARRAYSIZE(pgrs_timer_names)) + return pgrs_timer_names[(size_t)timer]; + return "?"; +} +#endif /* CURLVERBOSE */ + /* * Curl_pgrsTimeWas(). Store the timestamp time at the given label. */ @@ -285,7 +309,6 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, delta = &data->progress.t_pretransfer; break; 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 @@ -293,12 +316,12 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, * changing the t_starttransfer time. */ if(data->progress.is_t_startransfer_set) { + CURL_TRC_M(data, "[%s] ignored", pgrs_timer_name(timer)); return; } - else { - data->progress.is_t_startransfer_set = TRUE; - break; - } + data->progress.is_t_startransfer_set = TRUE; + delta = &data->progress.t_starttransfer; + break; case TIMER_POSTRANSFER: delta = &data->progress.t_posttransfer; break; @@ -314,7 +337,11 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, if(us < 1) us = 1; /* make sure at least one microsecond passed */ *delta += us; + CURL_TRC_M(data, "[%s] added %" FMT_TIMEDIFF_T "ns", + pgrs_timer_name(timer), us); } + else + CURL_TRC_M(data, "[%s] set", pgrs_timer_name(timer)); } /* @@ -703,3 +730,9 @@ void Curl_pgrsUpdate_nometer(struct Curl_easy *data) { (void)progress_calc(data, Curl_pgrs_now(data)); } + +void Curl_pgrsCompleted(struct Curl_easy *data) +{ + struct Progress * const p = &data->progress; + p->timespent = curlx_ptimediff_us(Curl_pgrs_now(data), &p->start); +} diff --git a/lib/progress.h b/lib/progress.h index 7d419ecb8ab0..1f2a4e71c305 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -83,4 +83,6 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent); +void Curl_pgrsCompleted(struct Curl_easy *data); + #endif /* HEADER_CURL_PROGRESS_H */ diff --git a/lib/protocol.c b/lib/protocol.c index c8d43251cf7e..8d57c058c6b7 100644 --- a/lib/protocol.c +++ b/lib/protocol.c @@ -153,7 +153,8 @@ const struct Curl_scheme Curl_scheme_https = { CURLPROTO_HTTPS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE | + PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTPS, /* defport */ }; @@ -361,6 +362,51 @@ const struct Curl_scheme Curl_scheme_smtps = { PORT_SMTPS, /* defport */ }; +const struct Curl_scheme Curl_scheme_socks = { + "socks", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks4 = { + "socks4", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks4a = { + "socks4a", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks5 = { + "socks5", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_socks5h = { + "socks5h", /* scheme */ + ZERO_NULL, + CURLPROTO_SOCKS, /* protocol */ + CURLPROTO_SOCKS, /* family */ + PROTOPT_NO_TRANSFER, /* flags */ + PORT_SOCKS, /* defport */ +}; + const struct Curl_scheme Curl_scheme_telnet = { "telnet", /* scheme */ #ifdef CURL_DISABLE_TELNET @@ -397,7 +443,7 @@ const struct Curl_scheme Curl_scheme_ws = { CURLPROTO_WS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL, + PROTOPT_USERPWDCTRL | PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTP /* defport */ }; @@ -412,7 +458,7 @@ const struct Curl_scheme Curl_scheme_wss = { CURLPROTO_WSS, /* protocol */ CURLPROTO_HTTP, /* family */ PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL, + PROTOPT_USERPWDCTRL | PROTOPT_HTTP_PROXY_TUNNEL, PORT_HTTPS /* defport */ }; @@ -430,49 +476,54 @@ const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) 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, + static const struct Curl_scheme * const all_schemes[59] = { NULL, + &Curl_scheme_pop3, NULL, + &Curl_scheme_smtps, + &Curl_scheme_socks, + &Curl_scheme_socks4, + &Curl_scheme_socks5, NULL, NULL, + &Curl_scheme_gophers, + &Curl_scheme_ws, + &Curl_scheme_sftp, + &Curl_scheme_socks4a, &Curl_scheme_scp, - &Curl_scheme_mqtts, - &Curl_scheme_imaps, + &Curl_scheme_rtsp, + &Curl_scheme_dict, NULL, NULL, + &Curl_scheme_gopher, NULL, NULL, NULL, + &Curl_scheme_wss, NULL, + &Curl_scheme_smb, NULL, &Curl_scheme_ldap, - &Curl_scheme_http, - &Curl_scheme_smb, NULL, NULL, - &Curl_scheme_telnet, + &Curl_scheme_ldaps, + &Curl_scheme_imap, NULL, NULL, NULL, + &Curl_scheme_imaps, &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, + &Curl_scheme_tftp, + &Curl_scheme_telnet, NULL, NULL, NULL, + &Curl_scheme_file, + &Curl_scheme_smtp, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &Curl_scheme_ftp, + &Curl_scheme_mqtt, NULL, + &Curl_scheme_socks5h, + &Curl_scheme_http, + &Curl_scheme_pop3s, NULL, + &Curl_scheme_mqtts, NULL, + &Curl_scheme_smbs, + &Curl_scheme_ftps, }; if(len && (len <= 7)) { const char *s = scheme; size_t l = len; const struct Curl_scheme *h; - unsigned int c = 792; + unsigned int c = 443; while(l) { - c <<= 4; + c <<= 5; c += (unsigned int)Curl_raw_tolower(*s); s++; l--; } - h = all_schemes[c % 47]; + h = all_schemes[c % 59]; if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) return h; } diff --git a/lib/protocol.h b/lib/protocol.h index f8254096e235..50e320d0f66c 100644 --- a/lib/protocol.h +++ b/lib/protocol.h @@ -51,6 +51,7 @@ struct easy_pollset; #define PORT_SMTPS 465 /* sometimes called SSMTP */ #define PORT_RTSP 554 #define PORT_GOPHER 70 +#define PORT_SOCKS 1080 #define PORT_MQTT 1883 #define PORT_MQTTS 8883 @@ -62,6 +63,7 @@ struct easy_pollset; #define CURLPROTO_WS (1L << 30) #define CURLPROTO_WSS ((curl_prot_t)1 << 31) #define CURLPROTO_MQTTS (1LL << 32) +#define CURLPROTO_SOCKS (1LL << 33) #define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff) @@ -103,6 +105,12 @@ typedef enum { FOLLOW_REDIR /* a full true redirect */ } followtype; +typedef enum { + DOMORE_GOBACK = -1, + DOMORE_INCOMPLETE = 0, + DOMORE_DONE = 1 +} domore; + /* * Specific protocol handler, an implementation of one or more URI schemes. */ @@ -118,9 +126,13 @@ struct Curl_protocol { /* 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. + * for doing the FTP stuff after the PASV/PORT command. The second + * argument is an output parameter that MUST be set to one of the + * DOMORE_* values: DOMORE_INCOMPLETE if more do_more work remains, + * DOMORE_DONE when the second phase is complete, or DOMORE_GOBACK + * to return to the regular DO/DOING handling. */ - CURLcode (*do_more)(struct Curl_easy *, int *); + CURLcode (*do_more)(struct Curl_easy *, domore *); /* 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. @@ -224,6 +236,9 @@ struct Curl_protocol { SSL connection in the same family without having PROTOPT_SSL. */ #define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ +#define PROTOPT_NO_TRANSFER (1 << 17) /* this protocol is not for transfers */ +#define PROTOPT_HTTP_PROXY_TUNNEL (1 << 18) /* Using this protocol with a + * HTTP proxy requires tunneling */ /* Everything about a URI scheme. */ struct Curl_scheme { @@ -268,6 +283,11 @@ 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_socks; +extern const struct Curl_scheme Curl_scheme_socks4; +extern const struct Curl_scheme Curl_scheme_socks4a; +extern const struct Curl_scheme Curl_scheme_socks5; +extern const struct Curl_scheme Curl_scheme_socks5h; extern const struct Curl_scheme Curl_scheme_telnet; extern const struct Curl_scheme Curl_scheme_tftp; extern const struct Curl_scheme Curl_scheme_ws; diff --git a/lib/proxy.c b/lib/proxy.c new file mode 100644 index 000000000000..920dc455986d --- /dev/null +++ b/lib/proxy.c @@ -0,0 +1,673 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 CURL_DISABLE_PROXY + +#include "urldata.h" +#include "curl_trc.h" +#include "protocol.h" +#include "proxy.h" +#include "http_proxy.h" +#include "strcase.h" +#include "url.h" +#include "vauth/vauth.h" +#include "curlx/inet_pton.h" +#include "curlx/strparse.h" + +#ifdef HAVE_NETINET_IN_H +#include +#endif + +#ifdef HAVE_ARPA_INET_H +#include +#endif + +/* + * cidr4_match() returns TRUE if the given IPv4 address is within the + * specified CIDR address range. + * + * @unittest 1614 + */ +UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits); +UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits) +{ + unsigned int address = 0; + unsigned int check = 0; + + if(bits > 32) + /* strange input */ + return FALSE; + + if(curlx_inet_pton(AF_INET, ipv4, &address) != 1) + return FALSE; + if(curlx_inet_pton(AF_INET, network, &check) != 1) + return FALSE; + + if(bits && (bits != 32)) { + unsigned int mask = 0xffffffff << (32 - bits); + unsigned int haddr = htonl(address); + unsigned int hcheck = htonl(check); +#if 0 + 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; + return TRUE; + } + return address == check; +} + +/* @unittest 1614 */ +UNITTEST bool cidr6_match(const char *ipv6, const char *network, + unsigned int bits); +UNITTEST bool cidr6_match(const char *ipv6, const char *network, + unsigned int bits) +{ +#ifdef USE_IPV6 + unsigned int bytes; + unsigned int rest; + unsigned char address[16]; + unsigned char check[16]; + + if(!bits) + bits = 128; + + bytes = bits / 8; + rest = bits & 0x07; + if((bytes > 16) || ((bytes == 16) && rest)) + return FALSE; + if(curlx_inet_pton(AF_INET6, ipv6, address) != 1) + return FALSE; + if(curlx_inet_pton(AF_INET6, network, check) != 1) + return FALSE; + if(bytes && memcmp(address, check, bytes)) + return FALSE; + if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) + return FALSE; + + return TRUE; +#else + (void)ipv6; + (void)network; + (void)bits; + return FALSE; +#endif +} + +enum nametype { + TYPE_HOST, + TYPE_IPV4, + 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 cidr6_match(name, checkip, bits); + else + return 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. + ****************************************************************/ +/* @unittest 1614 */ +UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy); +UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy) +{ + /* + * 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. + */ + if(!name || name[0] == '\0') + return FALSE; + + /* no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + */ + 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 only an asterisk */ + + /* 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 { + /* ignore trailing dots in the hostname */ + if(name[namelen - 1] == '.') + namelen--; + } + + while(*p) { + const char *token; + size_t tokenlen = 0; + + /* pass blanks */ + curlx_str_passblanks(&p); + + token = p; + /* pass over the pattern */ + while(*p && !ISBLANK(*p) && (*p != ',')) { + p++; + tokenlen++; + } + + if(tokenlen) { + bool match = FALSE; + if(type == TYPE_HOST) + match = match_host(token, tokenlen, name, namelen); + else + match = match_ip(type, token, tokenlen, name); + + if(match) + return TRUE; + } + + /* pass blanks after pattern */ + curlx_str_passblanks(&p); + /* if not a comma, this ends the loop */ + if(*p != ',') + break; + /* pass any number of commas */ + while(*p == ',') + p++; + } /* while(*p) */ + } /* NO_PROXY was specified and it was not only an asterisk */ + + return FALSE; +} + +#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. + ****************************************************************/ +static char *proxy_detect_proxy(struct Curl_easy *data, + const struct Curl_scheme *scheme) +{ + char *proxy = NULL; + + /* If proxy was not specified, we check for default proxy environment + * variables, to enable i.e Lynx compliance: + * + * http_proxy=http://some.server.dom:port/ + * https_proxy=http://some.server.dom:port/ + * ftp_proxy=http://some.server.dom:port/ + * no_proxy=domain1.dom,host.domain2.dom + * (a comma-separated list of hosts which should + * not be proxied, or an asterisk to override + * all proxy variables) + * all_proxy=http://some.server.dom:port/ + * (seems to exist for the CERN www lib. Probably + * the first to check for.) + * + * For compatibility, the all-uppercase versions of these variables are + * checked if the lowercase versions do not exist. + */ + char proxy_env[20]; + const char *envp; + VERBOSE(envp = proxy_env); + + curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", scheme->name); + + /* read the protocol proxy: */ + proxy = curl_getenv(proxy_env); + + /* + * We do not try the uppercase version of HTTP_PROXY because of + * security reasons: + * + * When curl is used in a webserver application + * environment (cgi or php), this environment variable can + * be controlled by the web server user by setting the + * http header 'Proxy:' to some value. + * + * This can cause 'internal' http/ftp requests to be + * arbitrarily redirected by any external attacker. + */ + if(!proxy && !curl_strequal("http_proxy", proxy_env)) { + /* There was no lowercase variable, try the uppercase version: */ + Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); + proxy = curl_getenv(proxy_env); + } + + if(!proxy) { +#ifndef CURL_DISABLE_WEBSOCKETS + /* websocket proxy fallbacks */ + if(curl_strequal("ws_proxy", proxy_env)) { + proxy = curl_getenv("http_proxy"); + } + else if(curl_strequal("wss_proxy", proxy_env)) { + proxy = curl_getenv("https_proxy"); + if(!proxy) + proxy = curl_getenv("HTTPS_PROXY"); + } + if(!proxy) { +#endif + envp = "all_proxy"; + proxy = curl_getenv(envp); /* default proxy to use */ + if(!proxy) { + envp = "ALL_PROXY"; + proxy = curl_getenv(envp); + } +#ifndef CURL_DISABLE_WEBSOCKETS + } +#endif + } + if(proxy) + infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); + + return proxy; +} +#endif /* CURL_DISABLE_HTTP */ + +/* + * If this is supposed to use a proxy, we need to figure out the proxy + * hostname, so that we can reuse an existing connection + * that may exist registered to the same proxy host. + */ +static CURLcode parse_proxy(struct Curl_easy *data, + const char *proxy, + bool for_pre_proxy, + struct proxy_info *proxyinfo) +{ + char *proxyuser = NULL; + char *proxypasswd = NULL; + char *scheme = NULL; + CURLcode result = CURLE_OK; + /* Set the start proxy type for URL scheme guessing */ + uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype; + CURLU *uhp = curl_url(); + CURLUcode uc; + + if(!uhp) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + /* 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); + if(!uc) { + /* parsed okay as a URL - only update proxytype when scheme was explicit */ + uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME); + if(!uc) { + result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy); + if(result) + goto error; + } + else if(uc != CURLUE_NO_SCHEME) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + /* else: no explicit scheme, keep the configured proxytype */ + } + else { + failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, + curl_url_strerror(uc)); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } + + result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype, + &proxyinfo->peer, &proxytype); + if(result) + goto error; + + switch(proxytype) { + case CURLPROXY_HTTP: + case CURLPROXY_HTTP_1_0: + case CURLPROXY_HTTPS: + case CURLPROXY_HTTPS2: + case CURLPROXY_HTTPS3: + if(for_pre_proxy) { + failf(data, "Unsupported pre-proxy type for \'%s\'", proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } + break; + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + break; + default: + failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto error; + } + + /* 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)) { + result = Curl_uc_to_curlcode(uc); + goto error; + } + uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); + if(uc && (uc != CURLUE_NO_PASSWORD)) { + result = Curl_uc_to_curlcode(uc); + goto error; + } + + if(proxyuser || proxypasswd) { + result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL, + data->set.str[STRING_PROXY_SERVICE_NAME], + CREDS_URL, &proxyinfo->creds); + if(result) + goto error; + } + else if(!for_pre_proxy && + (data->set.str[STRING_PROXYUSERNAME] || + data->set.str[STRING_PROXYPASSWORD] || + data->set.str[STRING_PROXY_SERVICE_NAME])) { + /* No user/passwd in URL, if this is not a pre-proxy, the + * CURLOPT_PROXY* settings apply. */ + result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME], + data->set.str[STRING_PROXYPASSWORD], + NULL, NULL, + data->set.str[STRING_PROXY_SERVICE_NAME], + CREDS_OPTION, &proxyinfo->creds); + } + else + Curl_creds_unlink(&proxyinfo->creds); + + proxyinfo->proxytype = proxytype; + +error: + curlx_free(scheme); + curlx_free(proxyuser); + curlx_free(proxypasswd); + curl_url_cleanup(uhp); +#ifdef DEBUGBUILD + if(!result) { + DEBUGASSERT(proxyinfo); + DEBUGASSERT(proxyinfo->peer); + } +#endif + return result; +} + +/* Is transfer's origin exempted from proxy use? */ +static bool proxy_do_not_proxy(struct Curl_easy *data) +{ + const char *no_proxy; + char *env_no_proxy = NULL; + bool do_not_proxy; + + /* no proxying if the transfer does not use the network */ + if(data->state.origin->scheme->flags & PROTOPT_NONETWORK) + return TRUE; + + no_proxy = data->set.str[STRING_NOPROXY]; + if(!no_proxy) { + const char *p = "no_proxy"; + env_no_proxy = curl_getenv(p); + if(!env_no_proxy) { + p = "NO_PROXY"; + env_no_proxy = curl_getenv(p); + } + if(env_no_proxy) + infof(data, "Uses proxy env variable %s == '%s'", p, env_no_proxy); + no_proxy = env_no_proxy; + } + + do_not_proxy = proxy_check_noproxy(data->state.origin->hostname, no_proxy); + curlx_safefree(env_no_proxy); + return do_not_proxy; +} + +CURLcode Curl_proxy_init_conn(struct Curl_easy *data, + struct connectdata *conn) +{ + char *proxy = NULL; + char *pre_proxy = NULL; + bool do_env_detect = TRUE; + CURLcode result = CURLE_OK; + + /* Enforce no proxy use unless we decide to use one */ + conn->bits.origin_is_proxy = FALSE; + DEBUGASSERT(!conn->socks_proxy.peer); + DEBUGASSERT(!conn->http_proxy.peer); + + if(proxy_do_not_proxy(data)) + goto out; + + /************************************************************* + * Detect what (if any) proxy to use + *************************************************************/ + /* the empty config strings disable proxy use and env detects */ + if(data->set.str[STRING_PROXY]) { + if(*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"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else + do_env_detect = FALSE; + } + + if(data->set.str[STRING_PRE_PROXY]) { + if(*data->set.str[STRING_PRE_PROXY]) { + pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); + /* if global socks proxy is set, this is it */ + if(!pre_proxy) { + failf(data, "memory shortage"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + else + do_env_detect = FALSE; + } + +#ifndef CURL_DISABLE_HTTP + /* None configured, detect possible proxy from environment. */ + if(!proxy && !pre_proxy && do_env_detect) + proxy = proxy_detect_proxy(data, conn->scheme); +#else + (void)do_env_detect; +#endif /* CURL_DISABLE_HTTP */ + + if(!proxy && !pre_proxy) + goto out; + + if(pre_proxy) { + result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy); + if(result) + goto out; + } + + if(proxy) { + result = parse_proxy(data, proxy, FALSE, &conn->http_proxy); + if(result) + goto out; + + switch(conn->http_proxy.proxytype) { + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + /* Whoops, it is not an HTTP proxy */ + if(pre_proxy) { + /* and we already have a SOCKS pre-proxy. Cannot have both */ + failf(data, "Having a SOCKS pre-proxy and proxy is not " + "supported with \'%s\'", proxy); + result = CURLE_COULDNT_RESOLVE_PROXY; + goto out; + } + /* switch */ + conn->socks_proxy = conn->http_proxy; + memset(&conn->http_proxy, 0, sizeof(conn->http_proxy)); + break; + default: + /* all other types are HTTP */ + break; + } + } + + if(conn->socks_proxy.peer) { + DEBUGASSERT(!CURL_PROXY_IS_ANY_HTTP(conn->socks_proxy.proxytype)); + } + +#ifdef CURL_DISABLE_HTTP + if(conn->http_proxy.peer) { + /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + +#else /* CURL_DISABLE_HTTP */ + if(conn->http_proxy.peer) { + const struct Curl_scheme *scheme = data->state.origin->scheme; + bool tunnel_proxy = (bool)data->set.tunnel_thru_httpproxy; + DEBUGASSERT(CURL_PROXY_IS_ANY_HTTP(conn->http_proxy.proxytype)); + + if(!tunnel_proxy) { + /* Decide if we tunnel through proxy automatically */ + if(conn->via_peer) { + /* With connect-to, we always tunnel */ + tunnel_proxy = TRUE; + } + else if(scheme->flags & PROTOPT_SSL) { + /* If the transfer is supposed to be secure, we tunnel */ + tunnel_proxy = TRUE; + } + else if(scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) { + /* transfer scheme required tunneling */ + tunnel_proxy = TRUE; + } + else if(!(scheme->protocol & PROTO_FAMILY_HTTP) && + !(scheme->flags & PROTOPT_PROXY_AS_HTTP)) { + /* Cannot delegate transfer URL to HTTP proxy */ + tunnel_proxy = TRUE; + } + } + + if(!tunnel_proxy) { + /* HTTP proxy used in forwarding mode. This means the connection + * is really to the proxy and NOT the origin of the transfer. */ + DEBUGASSERT(!conn->via_peer); + Curl_peer_link(&conn->origin, conn->http_proxy.peer); + conn->scheme = conn->http_proxy.peer->scheme; + conn->bits.origin_is_proxy = TRUE; + } + +#ifndef CURL_DISABLE_DIGEST_AUTH + if(!Curl_safecmp(data->state.envproxy, proxy)) { + /* proxy changed */ + Curl_auth_digest_cleanup(&data->state.proxydigest); + curlx_free(data->state.envproxy); + data->state.envproxy = curlx_strdup(proxy); + } +#endif + } +#endif /* !CURL_DISABLE_HTTP */ + +out: + curlx_free(pre_proxy); + curlx_free(proxy); + return result; +} + +#endif /* CURL_DISABLE_PROXY */ diff --git a/lib/proxy.h b/lib/proxy.h new file mode 100644 index 000000000000..307ee003e6fa --- /dev/null +++ b/lib/proxy.h @@ -0,0 +1,59 @@ +#ifndef HEADER_CURL_PROXY_H +#define HEADER_CURL_PROXY_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" + +#ifndef CURL_DISABLE_PROXY + +struct Curl_easy; +struct Curl_peer; +struct Curl_creds; +struct connectdata; + +struct proxy_info { + struct Curl_peer *peer; /* proxy to this peer */ + struct Curl_creds *creds; /* use these credentials, maybe NULL */ + uint8_t proxytype; /* what kind of proxy that is in use */ +}; + +#define CURL_PROXY_IS_HTTPS(t) \ + (((t) == CURLPROXY_HTTPS) || \ + ((t) == CURLPROXY_HTTPS2) || \ + ((t) == CURLPROXY_HTTPS3)) + +#define CURL_PROXY_IS_HTTP(t) \ + (((t) == CURLPROXY_HTTP) || \ + ((t) == CURLPROXY_HTTP_1_0)) + +#define CURL_PROXY_IS_ANY_HTTP(t) \ + (CURL_PROXY_IS_HTTP(t) || \ + CURL_PROXY_IS_HTTPS(t)) + +CURLcode Curl_proxy_init_conn(struct Curl_easy *data, + struct connectdata *conn); + +#endif /* !CURL_DISABLE_PROXY */ + +#endif /* HEADER_CURL_PROXY_H */ diff --git a/lib/psl.c b/lib/psl.c index e2488aea2215..195841f3a3f4 100644 --- a/lib/psl.c +++ b/lib/psl.c @@ -29,6 +29,10 @@ #include "progress.h" #include "curl_share.h" +#if !defined(PSL_VERSION_NUMBER) || PSL_VERSION_NUMBER < 0x001000 +#error "libpsl 0.16.0 or greater required" +#endif + void Curl_psl_destroy(struct PslCache *pslcache) { if(pslcache->psl) { @@ -65,17 +69,14 @@ const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy) bool dynamic = FALSE; time_t expires = TIME_T_MAX; -#if defined(PSL_VERSION_NUMBER) && PSL_VERSION_NUMBER >= 0x001000 psl = psl_latest(NULL); - dynamic = psl != NULL; + dynamic = !!psl; /* Take care of possible time computation overflow. */ 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) -#endif - psl = psl_builtin(); if(psl) { diff --git a/lib/rand.c b/lib/rand.c index dd82750ba6b4..3260fe3345bf 100644 --- a/lib/rand.c +++ b/lib/rand.c @@ -186,7 +186,7 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, /* * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random - * hexadecimal digits PLUS a null-terminating byte. It must be an odd number + * hexadecimal digits PLUS a null-terminator byte. It must be an odd number * size. */ @@ -214,7 +214,7 @@ 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 - * alphanumerical chars PLUS a null-terminating byte. + * alphanumerical chars PLUS a null-terminator byte. */ static const char alnum[] = diff --git a/lib/rand.h b/lib/rand.h index afccd0aac13b..a02717074fdb 100644 --- a/lib/rand.h +++ b/lib/rand.h @@ -37,14 +37,14 @@ CURLcode Curl_rand_bytes(struct Curl_easy *data, /* * Curl_rand_hex() fills the 'rnd' buffer with a given 'num' size with random - * hexadecimal digits PLUS a null-terminating byte. It must be an odd number + * hexadecimal digits PLUS a null-terminator byte. It must be an odd number * size. */ 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 - * alphanumerical chars PLUS a null-terminating byte. + * alphanumerical chars PLUS a null-terminator byte. */ CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd, size_t num); diff --git a/lib/ratelimit.c b/lib/ratelimit.c index dc013757e912..97af01c78801 100644 --- a/lib/ratelimit.c +++ b/lib/ratelimit.c @@ -141,12 +141,14 @@ static void rlimit_tune_steps(struct Curl_rlimit *r, 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) { + curl_off_t burst_inc = ((r->burst_per_step * mstep_inc) / 1000); + if(burst_inc) + r->burst_per_step += burst_inc; + } } } } - - if(r->burst_per_step) - r->burst_per_step = r->rate_per_step; } void Curl_rlimit_init(struct Curl_rlimit *r, diff --git a/lib/request.c b/lib/request.c index c414383dc068..56dd2c4a15c9 100644 --- a/lib/request.c +++ b/lib/request.c @@ -65,9 +65,9 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req, req->httpversion = 0; req->sendbuf_hds_len = 0; - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif result = Curl_client_start(data); @@ -115,9 +115,9 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) struct curltime t0 = { 0, 0 }; curlx_safefree(req->newurl); - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif #ifndef CURL_DISABLE_COOKIES curlx_safefree(req->cookiehost); @@ -175,9 +175,9 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data) void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) { curlx_safefree(req->newurl); - curlx_safefree(req->userpwd); + curlx_safefree(req->hd_auth); #ifndef CURL_DISABLE_PROXY - curlx_safefree(req->proxyuserpwd); + curlx_safefree(req->hd_proxy_auth); #endif if(req->sendbuf_init) Curl_bufq_free(&req->sendbuf); @@ -271,7 +271,8 @@ static CURLcode req_set_upload_done(struct Curl_easy *data) data->req.upload_done = TRUE; CURL_REQ_CLEAR_SEND(data); - Curl_pgrsTime(data, TIMER_POSTRANSFER); + if(data->mstate >= MSTATE_DID) + Curl_pgrsTime(data, TIMER_POSTRANSFER); Curl_creader_done(data, data->req.upload_aborted); if(data->req.upload_aborted) { @@ -333,7 +334,7 @@ static CURLcode req_flush(struct Curl_easy *data) result = Curl_xfer_send_shutdown(data, &done); if(result && data->req.shutdown_err_ignore) { infof(data, "Shutdown send direction error: %d. Broken server? " - "Proceeding as if everything is ok.", result); + "Proceeding as if everything is ok.", (int)result); result = CURLE_OK; done = TRUE; } @@ -392,7 +393,7 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req, blen = curlx_dyn_len(req); /* if the sendbuf is empty and the request without body and * the length to send fits info a sendbuf chunk, we send it directly. - * If `blen` is larger then `chunk_size`, we can not. Because we + * If `blen` is larger than `chunk_size`, we can not. Because we * might have to retry a blocked send later from sendbuf and that * would result in retry sends with a shrunken length. That is trouble. */ if(Curl_bufq_is_empty(&data->req.sendbuf) && diff --git a/lib/request.h b/lib/request.h index 6948d79be763..e67865a98433 100644 --- a/lib/request.h +++ b/lib/request.h @@ -114,9 +114,9 @@ struct SingleRequest { wanted */ uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */ - char *userpwd; /* auth header */ + char *hd_auth; /* Authorization header, full HTTP/1.x line */ #ifndef CURL_DISABLE_PROXY - char *proxyuserpwd; /* proxy auth header */ + char *hd_proxy_auth; /* Proxy-Authorization header, full HTTP/1.x line */ #endif #ifndef CURL_DISABLE_COOKIES char *cookiehost; diff --git a/lib/rtsp.c b/lib/rtsp.c index b08767f377cd..8c5cdd564388 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -288,8 +288,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) const char *p_stream_uri = NULL; const char *p_transport = NULL; const char *p_uagent = NULL; - const char *p_proxyuserpwd = NULL; - const char *p_userpwd = NULL; + const char *p_hd_proxy_auth = NULL; + const char *p_hd_auth = NULL; *done = TRUE; if(!rtsp) @@ -301,18 +301,6 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq; rtsp->CSeq_recv = 0; - /* Setup the first_* fields to allow auth details get sent - to this origin */ - - if(!data->state.first_host) { - 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->scheme->protocol; - } - /* Setup the 'p_request' pointer to the proper p_request string * Since all RTSP requests are included here, there is no need to * support custom requests like HTTP. @@ -448,14 +436,14 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) /* setup the authentication headers */ result = Curl_http_output_auth(data, conn, p_request, HTTPREQ_GET, - p_stream_uri, FALSE); + p_stream_uri, NULL, FALSE); if(result) goto out; #ifndef CURL_DISABLE_PROXY - p_proxyuserpwd = data->req.proxyuserpwd; + p_hd_proxy_auth = data->req.hd_proxy_auth; #endif - p_userpwd = data->req.userpwd; + p_hd_auth = data->req.hd_auth; /* Referrer */ curlx_safefree(data->state.aptr.ref); @@ -526,8 +514,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) "%s" /* range */ "%s" /* referrer */ "%s" /* user-agent */ - "%s" /* proxyuserpwd */ - "%s" /* userpwd */ + "%s" /* hd_proxy_auth */ + "%s" /* hd_auth */ , p_transport ? p_transport : "", p_accept ? p_accept : "", @@ -535,8 +523,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done) p_range ? p_range : "", p_referrer ? p_referrer : "", p_uagent ? p_uagent : "", - p_proxyuserpwd ? p_proxyuserpwd : "", - p_userpwd ? p_userpwd : ""); + p_hd_proxy_auth ? p_hd_proxy_auth : "", + p_hd_auth ? p_hd_auth : ""); if(result) goto out; @@ -670,7 +658,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data, while(blen && buf[0] != '$') { if(!in_body && buf[0] == 'R' && data->set.rtspreq != RTSPREQ_RECEIVE) { - if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) { + if(!strncmp(buf, "RTSP/", (blen < 5) ? blen : 5)) { /* This could be the next response, no consume and return */ if(*pconsumed) { DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " @@ -893,6 +881,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, result = rtsp_filter_rtp(data, rtspc, buf, blen, &consumed); if(result) goto out; + buf += consumed; blen -= consumed; } } @@ -904,7 +893,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data, * 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 ")", - blen, rtspc->in_header, data->req.done, rtspc->state, + blen, rtspc->in_header, data->req.done, (int)rtspc->state, data->req.size)); if(!result && (is_eos || blen)) { result = Curl_client_write(data, CLIENTWRITE_BODY | diff --git a/lib/select.h b/lib/select.h index 7552d0554865..95aed6c4fcaa 100644 --- a/lib/select.h +++ b/lib/select.h @@ -133,7 +133,7 @@ struct easy_pollset { #define CURL_EASY_POLLSET_MAGIC 0x7a657370 #endif -/* allocate and initialise */ +/* allocate and initialize */ struct easy_pollset *Curl_pollset_create(void); /* Initialize before first use */ diff --git a/lib/sendf.c b/lib/sendf.c index 7c977d3b9ea5..7559f64f84a7 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -103,7 +103,7 @@ CURLcode Curl_client_start(struct Curl_easy *data) result = r->crt->cntrl(data, r, CURL_CRCNTRL_REWIND); if(result) { failf(data, "rewind of client reader '%s' failed: %d", - r->crt->name, result); + r->crt->name, (int)result); return result; } r = r->next; @@ -183,7 +183,8 @@ static CURLcode cw_download_write(struct Curl_easy *data, bool is_connect = !!(type & CLIENTWRITE_CONNECT); if(!ctx->started_response && - !(type & (CLIENTWRITE_INFO | CLIENTWRITE_CONNECT))) { + !(type & CLIENTWRITE_CONNECT) && + (!(type & CLIENTWRITE_INFO) || data->req.upload_done)) { Curl_pgrsTime(data, TIMER_STARTTRANSFER); ctx->started_response = TRUE; } @@ -193,7 +194,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, return CURLE_OK; result = Curl_cwriter_write(data, writer->next, type, buf, nbytes); CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d", - type, nbytes, result); + (unsigned int)type, nbytes, (int)result); return result; } @@ -214,7 +215,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, /* BODY arrives although we want none, bail out */ streamclose(data->conn, "ignoring body"); CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), " - "did not want a BODY", type, nbytes); + "did not want a BODY", (unsigned int)type, nbytes); data->req.download_done = TRUE; if(data->info.header_size) /* if headers have been received, this is fine */ @@ -258,7 +259,7 @@ static CURLcode cw_download_write(struct Curl_easy *data, if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) { result = Curl_cwriter_write(data, writer->next, type, buf, nwrite); CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu) -> %d", - type, nbytes, result); + (unsigned int)type, nbytes, (int)result); if(result) return result; } @@ -396,7 +397,7 @@ CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, 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); + (unsigned int)type, len, (int)result); return result; } @@ -697,7 +698,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, case CURL_READFUNC_PAUSE: if(data->conn->scheme->flags & PROTOPT_NONETWORK) { /* protocols that work without network cannot be paused. This is - actually only FILE:// 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; @@ -731,7 +732,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, } 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, + blen, ctx->total_len, ctx->read_len, (int)result, *pnread, *peos); return result; } @@ -1054,7 +1055,7 @@ static CURLcode cr_lc_read(struct Curl_easy *data, out: CURL_TRC_READ(data, "cr_lc_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *pnread, *peos); + blen, (int)result, *pnread, *peos); return result; } @@ -1139,7 +1140,7 @@ CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len) result = do_init_reader_stack(data, r); out: CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d", - len, result); + len, (int)result); return result; } @@ -1217,7 +1218,7 @@ CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen, out: CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *nread, *eos); + blen, (int)result, *nread, *eos); return result; } @@ -1403,7 +1404,7 @@ CURLcode Curl_creader_set_buf(struct Curl_easy *data, cl_reset_reader(data); result = do_init_reader_stack(data, r); out: - CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, result); + CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, (int)result); return result; } @@ -1436,7 +1437,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); + CURL_TRC_READ(data, "unpausing %s -> %d", reader->crt->name, (int)result); if(result) break; reader = reader->next; diff --git a/lib/sendf.h b/lib/sendf.h index 75c6e248ea47..787fe7ff69ba 100644 --- a/lib/sendf.h +++ b/lib/sendf.h @@ -377,7 +377,7 @@ curl_off_t Curl_creader_client_length(struct Curl_easy *data); * values will be ignored. * @return CURLE_OK if offset could be set * CURLE_READ_ERROR if not supported by reader or seek/read failed - * of offset larger then total length + * of offset larger than total length * CURLE_PARTIAL_FILE if offset led to 0 total length */ CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset); diff --git a/lib/setopt.c b/lib/setopt.c index b8a632748c7a..eb9ff2e39679 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -108,7 +108,7 @@ CURLcode Curl_setblobopt(struct curl_blob **blobp, if(blob) { struct curl_blob *nblob; - if(!blob->len || (blob->len > CURL_MAX_INPUT_LENGTH)) + if(!blob->data || !blob->len || (blob->len > CURL_MAX_INPUT_LENGTH)) return CURLE_BAD_FUNCTION_ARGUMENT; nblob = (struct curl_blob *) curlx_malloc(sizeof(struct curl_blob) + @@ -240,17 +240,9 @@ static CURLcode httpauth(struct Curl_easy *data, bool proxy, if(auth != CURLAUTH_NONE) { int bitcheck = 0; bool authbits = FALSE; - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - bool iestyle = !!(auth & CURLAUTH_DIGEST_IE); - if(proxy) - data->state.authproxy.iestyle = iestyle; - else - data->state.authhost.iestyle = iestyle; - if(auth & CURLAUTH_DIGEST_IE) { auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ + auth &= ~CURLAUTH_DIGEST_IE; /* drop the legacy bit */ } /* switch off bits we cannot support */ @@ -334,7 +326,7 @@ CURLcode Curl_setopt_SSLVERSION(struct Curl_easy *data, CURLoption option, if(option != CURLOPT_SSLVERSION) primary = &data->set.proxy_ssl.primary; #else - if(option) {} + (void)option; /* unused */ #endif version = C_SSLVERSION_VALUE(arg); version_max = (long)C_SSLVERSION_MAX_VALUE(arg); @@ -407,22 +399,6 @@ static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg) } #endif /* !CURL_DISABLE_RTSP */ -#ifdef USE_SSL -static void set_ssl_options(struct ssl_config_data *ssl, - struct ssl_primary_config *config, - long arg) -{ - config->ssl_options = (unsigned char)(arg & 0xff); - ssl->enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); - ssl->no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - ssl->no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); - ssl->revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); - ssl->native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); - ssl->auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); - ssl->earlydata = !!(arg & CURLSSLOPT_EARLYDATA); -} -#endif - static CURLcode setopt_long_bool(struct Curl_easy *data, CURLoption option, long arg) { @@ -826,7 +802,7 @@ static CURLcode setopt_long_bool(struct Curl_easy *data, CURLoption option, if((arg > ok) || (arg < 0)) /* reserve other values for future use */ infof(data, "boolean setopt(%d) got unsupported argument %ld," - " treated as %d", option, arg, enabled); + " treated as %d", (int)option, arg, enabled); return CURLE_OK; } @@ -855,9 +831,9 @@ static CURLcode setopt_long_net(struct Curl_easy *data, CURLoption option, s->dns_cache_timeout_ms = -1; break; case CURLOPT_MAXCONNECTS: - result = value_range(&arg, 1, 1, INT_MAX); + result = value_range(&arg, 0, 0, INT_MAX); if(!result) - s->maxconnects = (uint32_t)arg; + s->maxconnects = arg ? (uint32_t)arg : DEFAULT_CONNCACHE_SIZE; break; case CURLOPT_SERVER_RESPONSE_TIMEOUT: return setopt_set_timeout_sec(&s->server_response_timeout, arg); @@ -1002,11 +978,11 @@ static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, s->use_ssl = (unsigned char)arg; break; case CURLOPT_SSL_OPTIONS: - set_ssl_options(&s->ssl, &s->ssl.primary, arg); + s->ssl.primary.ssl_options = (unsigned char)(arg & 0xff); break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSL_OPTIONS: - set_ssl_options(&s->proxy_ssl, &s->proxy_ssl.primary, arg); + s->proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff); break; #endif case CURLOPT_SSL_ENABLE_NPN: @@ -1027,23 +1003,35 @@ static CURLcode setopt_long_ssl(struct Curl_easy *data, CURLoption option, #endif /* !USE_SSL */ } +#ifndef CURL_DISABLE_PROXY +static void changeproxy(struct Curl_easy *data) +{ + Curl_auth_digest_cleanup(&data->state.proxydigest); + memset(&data->state.authproxy, 0, sizeof(data->state.authproxy)); +} + 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; + if(arg != s->proxyport) + changeproxy(data); 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)) + if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_HTTPS3)) return CURLE_BAD_FUNCTION_ARGUMENT; +#ifndef USE_PROXY_HTTP3 + if(arg == CURLPROXY_HTTPS3) + return CURLE_NOT_BUILT_IN; +#endif s->proxytype = (unsigned char)arg; break; case CURLOPT_SOCKS5_AUTH: @@ -1055,13 +1043,17 @@ static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, return CURLE_UNKNOWN_OPTION; } return CURLE_OK; +} #else +static CURLcode setopt_long_proxy(struct Curl_easy *data, CURLoption option, + long arg) +{ (void)data; (void)option; (void)arg; return CURLE_UNKNOWN_OPTION; -#endif } +#endif static CURLcode setopt_long_http(struct Curl_easy *data, CURLoption option, long arg) @@ -1276,15 +1268,23 @@ static CURLcode setopt_long_misc(struct Curl_easy *data, CURLoption option, return CURLE_OUT_OF_MEMORY; } } - else + else if(!data->share || !data->share->hsts) { + /* throw away the HSTS cache unless shared */ Curl_hsts_cleanup(&data->hsts); + /* flush all the entries */ + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; + } + else + /* detach from shared HSTS cache without freeing it */ + data->hsts = NULL; break; #endif #ifndef CURL_DISABLE_ALTSVC case CURLOPT_ALTSVC_CTRL: return Curl_altsvc_ctrl(data, arg); #endif -#ifdef HAVE_GSSAPI +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) case CURLOPT_GSSAPI_DELEGATION: s->gssapi_delegation = (unsigned char)arg & (CURLGSSAPI_DELEGATION_POLICY_FLAG | CURLGSSAPI_DELEGATION_FLAG); @@ -1515,13 +1515,10 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, #ifdef USE_HTTP2 case CURLOPT_STREAM_DEPENDS: - case CURLOPT_STREAM_DEPENDS_E: { - struct Curl_easy *dep = va_arg(param, struct Curl_easy *); - if(!dep || GOOD_EASY_HANDLE(dep)) - return Curl_data_priority_add_child(dep, data, - option == CURLOPT_STREAM_DEPENDS_E); + case CURLOPT_STREAM_DEPENDS_E: + /* not doing stream dependencies any longer, but accept options + * for backward compatibility */ break; - } #endif default: @@ -1630,13 +1627,12 @@ static CURLcode setproxy(struct Curl_easy *data, const char *proxy) !strcmp(data->set.str[STRING_PROXY], proxy)) return CURLE_OK; /* same one as before */ - Curl_auth_digest_cleanup(&data->state.proxydigest); - memset(&data->state.authproxy, 0, sizeof(data->state.authproxy)); + changeproxy(data); return Curl_setstropt(&data->set.str[STRING_PROXY], proxy); } static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, - const char *ptr) + char *ptr) { CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; @@ -1650,16 +1646,16 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, result = setstropt_userpwd(ptr, &u, &p); /* URL decode the components */ - if(!result && u) { + if(!result) { 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]); + if(u) + result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + } + if(!result && p) result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, REJECT_ZERO); - } curlx_free(u); curlx_free(p); break; @@ -1770,20 +1766,24 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, * 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); + result = Curl_setstropt(&s->str[STRING_SSL_CAFILE_PROXY], ptr); + s->proxy_ssl.custom_cafile = !!s->str[STRING_SSL_CAFILE_PROXY]; + return result; 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); + if(Curl_ssl_supports(data, SSLSUPP_CRLFILE)) + return Curl_setstropt(&s->str[STRING_SSL_CRLFILE_PROXY], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_ISSUERCERT: /* - * Set Issuer certificate file - * to check certificates issuer + * Set Issuer certificate file to check certificates issuer */ - return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT_PROXY], ptr); + return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_CAPATH: /* * Set CA path info for SSL connection proxy. Specify directory name of the @@ -1792,8 +1792,9 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, #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); + result = Curl_setstropt(&s->str[STRING_SSL_CAPATH_PROXY], ptr); + s->proxy_ssl.custom_capath = !!s->str[STRING_SSL_CAPATH_PROXY]; + return result; } #endif return CURLE_NOT_BUILT_IN; @@ -1813,14 +1814,12 @@ static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) { CURLcode result = CURLE_OK; + if(s->postfieldsize < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; 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); + size_t pflen = curlx_sotouz_range(s->postfieldsize, 0, SIZE_MAX); if(pflen == SIZE_MAX) return CURLE_OUT_OF_MEMORY; else { @@ -1844,37 +1843,79 @@ static CURLcode setopt_copypostfields(const char *ptr, struct UserDefined *s) } #endif -static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, - char *ptr) +#ifdef USE_ECH +static CURLcode setopt_ech(struct Curl_easy *data, const 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; + CURLcode result = CURLE_OK; + + if(!ptr || !strcmp(ptr, "false")) + s->tls_ech = CURLECH_DISABLE; + else { + size_t plen = strlen(ptr); + if(plen > CURL_MAX_INPUT_LENGTH) + result = CURLE_BAD_FUNCTION_ARGUMENT; + else { + if(!strcmp(ptr, "grease")) + s->tls_ech = CURLECH_GREASE; + else if(!strcmp(ptr, "true")) + s->tls_ech = CURLECH_ENABLE; + else if(!strcmp(ptr, "hard")) + s->tls_ech = CURLECH_HARD; + else if(plen > 4 && !strncmp(ptr, "ecl:", 4)) { + if(!s->tls_ech) + s->tls_ech = CURLECH_HARD; + result = Curl_setstropt(&s->str[STRING_ECH_CONFIG], ptr + 4); + } + else if(plen > 3 && !strncmp(ptr, "pn:", 3)) { + if(!s->tls_ech) + s->tls_ech = CURLECH_HARD; + result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); + } + else + result = CURLE_BAD_FUNCTION_ARGUMENT; + } + } + return result; +} +#else +#define setopt_ech(x,y) CURLE_NOT_BUILT_IN #endif - result = CURLE_OK; + +#if defined(USE_SSL) || defined(USE_SSH) +/* One of the options is used for both TLS and SSH */ +static CURLcode setopt_cptr_ssl(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; switch(option) { + case CURLOPT_KEYPASSWD: + /* + * String that holds the SSL or SSH private key password. + */ + result = Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); + break; +#ifdef USE_SSL 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); + result = Curl_setstropt(&s->str[STRING_SSL_CAFILE], ptr); + s->ssl.custom_cafile = !!s->str[STRING_SSL_CAFILE]; + return result; 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); + result = Curl_setstropt(&s->str[STRING_SSL_CAPATH], ptr); + s->ssl.custom_capath = !!s->str[STRING_SSL_CAPATH]; + return result; } -#endif return CURLE_NOT_BUILT_IN; case CURLOPT_CRLFILE: /* @@ -1891,27 +1932,106 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, else return CURLE_NOT_BUILT_IN; case CURLOPT_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { + if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) /* set preferred list of TLS 1.3 cipher suites */ return Curl_setstropt(&s->str[STRING_SSL_CIPHER13_LIST], ptr); - } else return CURLE_NOT_BUILT_IN; case CURLOPT_RANDOM_FILE: break; case CURLOPT_EGDSOCKET: break; - case CURLOPT_REQUEST_TARGET: - return Curl_setstropt(&s->str[STRING_TARGET], ptr); -#ifndef CURL_DISABLE_NETRC - case CURLOPT_NETRC_FILE: + case CURLOPT_SSL_CTX_DATA: /* - * Use this file instead of the $HOME/.netrc file + * Set an SSL_CTX callback parameter pointer */ - return Curl_setstropt(&s->str[STRING_NETRC_FILE], ptr); + if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) { + s->ssl.fsslctxp = ptr; + break; + } + else + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSLCERT: + /* + * String that holds filename of the SSL certificate to use + */ + return Curl_setstropt(&s->str[STRING_CERT], ptr); + case CURLOPT_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); + case CURLOPT_SSLKEY: + /* + * String that holds filename of the SSL key to use + */ + return Curl_setstropt(&s->str[STRING_KEY], ptr); + case CURLOPT_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use + */ + return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); + case CURLOPT_SSLENGINE: + /* + * String that holds the SSL crypto engine. + */ + if(ptr && ptr[0]) { + result = Curl_setstropt(&s->str[STRING_SSL_ENGINE], ptr); + if(!result) { + result = Curl_ssl_set_engine(data, ptr); + } + } + break; + case CURLOPT_ISSUERCERT: + /* + * Set Issuer certificate file + * to check certificates issuer + */ + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT)) + return Curl_setstropt(&s->str[STRING_SSL_ISSUERCERT], ptr); + return CURLE_NOT_BUILT_IN; + case CURLOPT_SSL_EC_CURVES: + /* + * Set accepted curves in SSL connection setup. + * Specify colon-delimited list of curve algorithm names. + */ + 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 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; + case CURLOPT_ECH: + return setopt_ech(data, ptr); +#endif + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} #endif #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) +static CURLcode setopt_cptr_http_mqtt(struct Curl_easy *data, + CURLoption option, char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { case CURLOPT_COPYPOSTFIELDS: return setopt_copypostfields(ptr, s); @@ -1924,7 +2044,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->method = HTTPREQ_POST; break; -#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ #ifndef CURL_DISABLE_HTTP case CURLOPT_TRAILERDATA: @@ -1970,6 +2089,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, /* * String to set in the HTTP Referer: field. */ + Curl_bufref_free(&data->state.referer); result = Curl_setstropt(&s->str[STRING_SET_REFERER], ptr); break; @@ -2013,311 +2133,238 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #endif /* !CURL_DISABLE_COOKIES */ #endif /* !CURL_DISABLE_HTTP */ + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} +#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ - case CURLOPT_CUSTOMREQUEST: - /* - * Set a custom string to use as request - */ - 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 changes the actual request - keyword */ - case CURLOPT_SERVICE_NAME: +#ifdef USE_SSH +static CURLcode setopt_cptr_ssh(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_SSH_PUBLIC_KEYFILE: /* - * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO + * Use this file instead of the $HOME/.ssh/id_dsa.pub file */ - return Curl_setstropt(&s->str[STRING_SERVICE_NAME], ptr); - - case CURLOPT_HEADERDATA: + return Curl_setstropt(&s->str[STRING_SSH_PUBLIC_KEY], ptr); + case CURLOPT_SSH_PRIVATE_KEYFILE: /* - * Custom pointer to pass the header write callback function + * Use this file instead of the $HOME/.ssh/id_dsa file */ - s->writeheader = ptr; - break; - case CURLOPT_READDATA: + return Curl_setstropt(&s->str[STRING_SSH_PRIVATE_KEY], ptr); + case CURLOPT_SSH_KEYDATA: /* - * FILE pointer to read the file to be uploaded from. Or possibly used as - * argument to the read callback. + * Custom client data to pass to the SSH keyfunc callback */ - s->in_set = ptr; + s->ssh_keyfunc_userp = ptr; break; - case CURLOPT_WRITEDATA: + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: /* - * FILE pointer to write to. Or possibly used as argument to the write - * callback. + * Option to allow for the MD5 of the host public key to be checked + * for validation purposes. */ - s->out = ptr; - break; - case CURLOPT_DEBUGDATA: + return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); + case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: /* - * Set to a void * that should receive all error writes. This - * defaults to CURLOPT_STDERR for normal operations. + * Option to allow for the SHA256 of the host public key to be checked + * for validation purposes. */ - s->debugdata = ptr; - break; - case CURLOPT_PROGRESSDATA: + return Curl_setstropt(&s->str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], ptr); + case CURLOPT_SSH_KNOWNHOSTS: /* - * Custom client data to pass to the progress callback + * Store the filename to read known hosts from. */ - s->progress_client = ptr; - break; - case CURLOPT_SEEKDATA: + return Curl_setstropt(&s->str[STRING_SSH_KNOWNHOSTS], ptr); +#ifdef USE_LIBSSH2 + case CURLOPT_SSH_HOSTKEYDATA: /* - * Seek control callback. Might be NULL. + * Custom client data to pass to the SSH keyfunc callback */ - s->seek_client = ptr; + s->ssh_hostkeyfunc_userp = ptr; break; - case CURLOPT_IOCTLDATA: +#endif /* USE_LIBSSH2 */ + default: + return CURLE_UNKNOWN_OPTION; + } + return CURLE_OK; +} +#endif /* USE_SSH */ + +#ifndef CURL_DISABLE_FTP +static CURLcode setopt_cptr_ftp(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_FTPPORT: /* - * I/O control data pointer. Might be NULL. + * Use FTP PORT, this also specifies which IP address to use */ - s->ioctl_client = ptr; + result = Curl_setstropt(&s->str[STRING_FTPPORT], ptr); + s->ftp_use_port = !!(s->str[STRING_FTPPORT]); break; - case CURLOPT_SSL_CTX_DATA: - /* - * Set an SSL_CTX callback parameter pointer - */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) { - s->ssl.fsslctxp = ptr; - break; + + case CURLOPT_FTP_ACCOUNT: + return Curl_setstropt(&s->str[STRING_FTP_ACCOUNT], ptr); + + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + return Curl_setstropt(&s->str[STRING_FTP_ALTERNATIVE_TO_USER], ptr); + + case CURLOPT_KRBLEVEL: + return CURLE_NOT_BUILT_IN; /* removed in 8.17.0 */ + case CURLOPT_CHUNK_DATA: + s->wildcardptr = ptr; + break; + case CURLOPT_FNMATCH_DATA: + s->fnmatch_data = ptr; + break; + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} +#endif /* !CURL_DISABLE_FTP */ + +static CURLcode setopt_cptr_net(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + struct UserDefined *s = &data->set; + switch(option) { + case CURLOPT_INTERFACE: + /* + * Set what interface or address/hostname to bind the socket to when + * performing an operation and thus what from-IP your connection will use. + */ + return setstropt_interface(ptr, + &s->str[STRING_DEVICE], + &s->str[STRING_INTERFACE], + &s->str[STRING_BINDHOST]); +#ifdef USE_RESOLV_ARES + case CURLOPT_DNS_SERVERS: + return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); + + case CURLOPT_DNS_INTERFACE: + return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); + + case CURLOPT_DNS_LOCAL_IP4: + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); + + case CURLOPT_DNS_LOCAL_IP6: + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); +#endif +#ifdef USE_UNIX_SOCKETS + case CURLOPT_UNIX_SOCKET_PATH: + s->abstract_unix_socket = FALSE; + return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); + + case CURLOPT_ABSTRACT_UNIX_SOCKET: + s->abstract_unix_socket = TRUE; + return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); +#endif +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_URL: + { + CURLcode result = Curl_setstropt(&s->str[STRING_DOH], ptr); + s->doh = !!(s->str[STRING_DOH]); + return result; } - else #endif - return CURLE_NOT_BUILT_IN; + default: + return CURLE_UNKNOWN_OPTION; + } +} + +static CURLcode setopt_cptr_misc(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + struct UserDefined *s = &data->set; + + switch(option) { + case CURLOPT_REQUEST_TARGET: + return Curl_setstropt(&s->str[STRING_TARGET], ptr); +#ifndef CURL_DISABLE_NETRC + case CURLOPT_NETRC_FILE: + return Curl_setstropt(&s->str[STRING_NETRC_FILE], ptr); +#endif + case CURLOPT_CUSTOMREQUEST: + 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 changes the actual request + keyword */ + case CURLOPT_SERVICE_NAME: + return Curl_setstropt(&s->str[STRING_SERVICE_NAME], ptr); + + case CURLOPT_HEADERDATA: + s->writeheader = ptr; + break; + case CURLOPT_READDATA: + s->in_set = ptr; + break; + case CURLOPT_WRITEDATA: + s->out = ptr; + break; + case CURLOPT_DEBUGDATA: + s->debugdata = ptr; + break; + case CURLOPT_PROGRESSDATA: + s->progress_client = ptr; + break; + case CURLOPT_SEEKDATA: + s->seek_client = ptr; + break; + case CURLOPT_IOCTLDATA: + s->ioctl_client = ptr; + break; case CURLOPT_SOCKOPTDATA: - /* - * socket callback data pointer. Might be NULL. - */ s->sockopt_client = ptr; break; case CURLOPT_OPENSOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ s->opensocket_client = ptr; break; case CURLOPT_RESOLVER_START_DATA: - /* - * resolver start callback data pointer. Might be NULL. - */ s->resolver_start_client = ptr; break; case CURLOPT_CLOSESOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ s->closesocket_client = ptr; break; case CURLOPT_PREREQDATA: s->prereq_userp = ptr; break; case CURLOPT_ERRORBUFFER: - /* - * Error buffer provided by the caller to get the human readable error - * string in. - */ s->errorbuffer = ptr; break; - -#ifndef CURL_DISABLE_FTP - case CURLOPT_FTPPORT: - /* - * Use FTP PORT, this also specifies which IP address to use - */ - result = Curl_setstropt(&s->str[STRING_FTPPORT], ptr); - s->ftp_use_port = !!(s->str[STRING_FTPPORT]); - break; - - case CURLOPT_FTP_ACCOUNT: - return Curl_setstropt(&s->str[STRING_FTP_ACCOUNT], ptr); - - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - return Curl_setstropt(&s->str[STRING_FTP_ALTERNATIVE_TO_USER], ptr); - - case CURLOPT_KRBLEVEL: - return CURLE_NOT_BUILT_IN; /* removed in 8.17.0 */ - case CURLOPT_CHUNK_DATA: - s->wildcardptr = ptr; - break; - case CURLOPT_FNMATCH_DATA: - s->fnmatch_data = ptr; - break; -#endif case CURLOPT_URL: - /* - * The URL to fetch. - */ result = Curl_setstropt(&s->str[STRING_SET_URL], ptr); Curl_bufref_set(&data->state.url, s->str[STRING_SET_URL], 0, NULL); break; case CURLOPT_USERPWD: - /* - * user:password to use in the operation - */ return setstropt_userpwd(ptr, &s->str[STRING_USERNAME], &s->str[STRING_PASSWORD]); case CURLOPT_USERNAME: - /* - * authentication username to use in the operation - */ return Curl_setstropt(&s->str[STRING_USERNAME], ptr); case CURLOPT_PASSWORD: - /* - * authentication password to use in the operation - */ return Curl_setstropt(&s->str[STRING_PASSWORD], ptr); case CURLOPT_LOGIN_OPTIONS: - /* - * authentication options to use in the operation - */ return Curl_setstropt(&s->str[STRING_OPTIONS], ptr); case CURLOPT_XOAUTH2_BEARER: - /* - * OAuth 2.0 bearer token to use in the operation - */ return Curl_setstropt(&s->str[STRING_BEARER], ptr); case CURLOPT_RANGE: - /* - * What range of the file you want to transfer - */ return Curl_setstropt(&s->str[STRING_SET_RANGE], ptr); - case CURLOPT_SSLCERT: - /* - * String that holds filename of the SSL certificate to use - */ - return Curl_setstropt(&s->str[STRING_CERT], ptr); - case CURLOPT_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use - */ - return Curl_setstropt(&s->str[STRING_CERT_TYPE], ptr); - case CURLOPT_SSLKEY: - /* - * String that holds filename of the SSL key to use - */ - return Curl_setstropt(&s->str[STRING_KEY], ptr); - case CURLOPT_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use - */ - return Curl_setstropt(&s->str[STRING_KEY_TYPE], ptr); - case CURLOPT_KEYPASSWD: - /* - * String that holds the SSL or SSH private key password. - */ - return Curl_setstropt(&s->str[STRING_KEY_PASSWD], ptr); - case CURLOPT_SSLENGINE: - /* - * String that holds the SSL crypto engine. - */ - if(ptr && ptr[0]) { - result = Curl_setstropt(&s->str[STRING_SSL_ENGINE], ptr); - if(!result) { - result = Curl_ssl_set_engine(data, ptr); - } - } - break; - case CURLOPT_INTERFACE: - /* - * Set what interface or address/hostname to bind the socket to when - * performing an operation and thus what from-IP your connection will use. - */ - return setstropt_interface(ptr, - &s->str[STRING_DEVICE], - &s->str[STRING_INTERFACE], - &s->str[STRING_BINDHOST]); - case CURLOPT_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - 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. - */ - 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 CURLE_NOT_BUILT_IN; - case CURLOPT_PINNEDPUBLICKEY: - /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. - */ - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - return Curl_setstropt(&s->str[STRING_SSL_PINNEDPUBLICKEY], ptr); - return CURLE_NOT_BUILT_IN; -#endif -#ifdef USE_SSH - case CURLOPT_SSH_PUBLIC_KEYFILE: - /* - * 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: - /* - * Option to allow for the MD5 of the host public key to be checked - * 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 -#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); - case CURLOPT_SSH_HOSTKEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - s->ssh_hostkeyfunc_userp = ptr; - break; -#endif /* USE_LIBSSH2 */ -#endif /* USE_SSH */ case CURLOPT_PROTOCOLS_STR: if(ptr) { curl_prot_t protos; @@ -2356,21 +2403,10 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return Curl_setstropt(&s->str[STRING_SASL_AUTHZID], ptr); #ifndef CURL_DISABLE_RTSP case CURLOPT_RTSP_SESSION_ID: - /* - * Set the RTSP Session ID manually. Useful if the application is - * 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; @@ -2396,37 +2432,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, break; #endif #endif -#ifdef USE_RESOLV_ARES - case CURLOPT_DNS_SERVERS: - return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); - - case CURLOPT_DNS_INTERFACE: - return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); - - case CURLOPT_DNS_LOCAL_IP4: - return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); - - case CURLOPT_DNS_LOCAL_IP6: - return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); - -#endif -#ifdef USE_UNIX_SOCKETS - case CURLOPT_UNIX_SOCKET_PATH: - s->abstract_unix_socket = FALSE; - return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); - - case CURLOPT_ABSTRACT_UNIX_SOCKET: - s->abstract_unix_socket = TRUE; - return Curl_setstropt(&s->str[STRING_UNIX_SOCKET_PATH], ptr); - -#endif - -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_URL: - result = Curl_setstropt(&s->str[STRING_DOH], ptr); - s->doh = !!(s->str[STRING_DOH]); - break; -#endif #ifndef CURL_DISABLE_HSTS case CURLOPT_HSTSREADDATA: s->hsts_read_userp = ptr; @@ -2481,44 +2486,48 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return Curl_altsvc_load(data->asi, ptr); break; #endif /* !CURL_DISABLE_ALTSVC */ -#ifdef USE_ECH - case CURLOPT_ECH: { - size_t plen = 0; - - if(!ptr) { - s->tls_ech = CURLECH_DISABLE; - break; - } - plen = strlen(ptr); - if(plen > CURL_MAX_INPUT_LENGTH) { - s->tls_ech = CURLECH_DISABLE; - return CURLE_BAD_FUNCTION_ARGUMENT; - } - /* set tls_ech flag value, preserving CLA_CFG bit */ - if(!strcmp(ptr, "false")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_DISABLE; - else if(!strcmp(ptr, "grease")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_GREASE; - else if(!strcmp(ptr, "true")) - s->tls_ech = (s->tls_ech & CURLECH_CLA_CFG) | CURLECH_ENABLE; - else if(!strcmp(ptr, "hard")) - 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) - s->tls_ech |= CURLECH_CLA_CFG; - } - else if(plen > 4 && !strncmp(ptr, "pn:", 3)) - result = Curl_setstropt(&s->str[STRING_ECH_PUBLIC], ptr + 3); - break; - } -#endif + case CURLOPT_ECH: + return setopt_ech(data, ptr); default: return CURLE_UNKNOWN_OPTION; } return result; } +static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + typedef CURLcode (*ptrfunc)(struct Curl_easy *data, CURLoption option, + char *ptr); + static const ptrfunc setopt_call[] = { +#ifndef CURL_DISABLE_PROXY + setopt_cptr_proxy, +#endif +#if defined(USE_SSL) || defined(USE_SSH) + setopt_cptr_ssl, +#endif +#ifdef USE_SSH + setopt_cptr_ssh, +#endif +#ifndef CURL_DISABLE_FTP + setopt_cptr_ftp, +#endif +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) + setopt_cptr_http_mqtt, +#endif + setopt_cptr_net, + setopt_cptr_misc, + }; + size_t i; + + for(i = 0; i < CURL_ARRAYSIZE(setopt_call); i++) { + CURLcode result = setopt_call[i](data, option, ptr); + if(result != CURLE_UNKNOWN_OPTION) + return result; + } + return CURLE_UNKNOWN_OPTION; +} + static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, va_list param) { @@ -2749,13 +2758,15 @@ static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, break; case CURLOPT_MAX_SEND_SPEED_LARGE: /* - * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE + * When transfer uploads are faster than CURLOPT_MAX_SEND_SPEED_LARGE * bytes per second the transfer is throttled.. */ if(offt < 0) return CURLE_BAD_FUNCTION_ARGUMENT; s->max_send_speed = offt; - Curl_rlimit_init(&data->progress.ul.rlimit, offt, offt, + /* use minimal burst rate of 32k. some protocol batch IO */ + Curl_rlimit_init(&data->progress.ul.rlimit, offt, + CURLMAX(offt, (32 * 1024)), Curl_pgrs_now(data)); break; case CURLOPT_MAX_RECV_SPEED_LARGE: @@ -2766,7 +2777,9 @@ 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, + /* use minimal burst rate of 32k. some protocol batch IO */ + Curl_rlimit_init(&data->progress.dl.rlimit, offt, + CURLMAX(offt, (32 * 1024)), Curl_pgrs_now(data)); break; case CURLOPT_RESUME_FROM_LARGE: @@ -2819,15 +2832,20 @@ 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)) - return Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob); + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { + CURLcode result = Curl_setblobopt(&s->blobs[BLOB_CAINFO_PROXY], blob); + s->proxy_ssl.custom_cablob = !!s->blobs[BLOB_CAINFO_PROXY]; + return result; + } #endif return CURLE_NOT_BUILT_IN; case CURLOPT_PROXY_ISSUERCERT_BLOB: /* * Blob that holds Issuer certificate to check certificates issuer */ - return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT_PROXY], blob); + if(Curl_ssl_supports(data, SSLSUPP_ISSUERCERT_BLOB)) + return Curl_setblobopt(&s->blobs[BLOB_SSL_ISSUERCERT_PROXY], blob); + return CURLE_NOT_BUILT_IN; #endif case CURLOPT_SSLKEY_BLOB: /* @@ -2841,8 +2859,9 @@ static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, */ #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { - s->ssl.custom_cablob = TRUE; - return Curl_setblobopt(&s->blobs[BLOB_CAINFO], blob); + CURLcode result = Curl_setblobopt(&s->blobs[BLOB_CAINFO], blob); + s->ssl.custom_cablob = !!s->blobs[BLOB_CAINFO]; + return result; } #endif return CURLE_NOT_BUILT_IN; @@ -2928,6 +2947,6 @@ CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) va_end(arg); if(result == CURLE_BAD_FUNCTION_ARGUMENT) - failf(data, "setopt 0x%x got bad argument", option); + failf(data, "setopt 0x%x got bad argument", (unsigned int)option); return result; } diff --git a/lib/sha256.c b/lib/sha256.c index d97f45f05f9b..047119044bb8 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -43,8 +43,8 @@ * 2. USE_WOLFSSL * 3. USE_GNUTLS * 4. USE_MBEDTLS - * 5. USE_COMMON_CRYPTO - * 6. USE_WIN32_CRYPTO + * 5. USE_WIN32_CRYPTO + * 6. USE_COMMON_CRYPTO * * This ensures that the same SSL branch gets activated throughout this source * file even if multiple backends are enabled at the same time. @@ -113,7 +113,8 @@ static void my_sha256_final(unsigned char *digest, void *in) } #elif defined(USE_GNUTLS) -#include +#include +#include typedef struct sha256_ctx my_sha256_ctx; @@ -132,11 +133,15 @@ static void my_sha256_update(void *ctx, static void my_sha256_final(unsigned char *digest, void *ctx) { +#if NETTLE_VERSION_MAJOR >= 4 + sha256_digest(ctx, digest); +#else sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); +#endif } #elif defined(USE_MBEDTLS) && \ - defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ + defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 #include typedef psa_hash_operation_t my_sha256_ctx; diff --git a/lib/smb.c b/lib/smb.c index 6a97d2e00685..70af8d597090 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -61,7 +61,7 @@ enum smb_conn_state { /* SMB connection data, kept at connection */ struct smb_conn { enum smb_conn_state state; - char *user; + const char *user; char *domain; char *share; unsigned char challenge[8]; @@ -467,14 +467,15 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) { struct connectdata *conn = data->conn; struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN); - char *slash; + const char *slash; + const char *user = Curl_creds_user(conn->creds); (void)done; if(!smbc) return CURLE_FAILED_INIT; /* Check we have a username and password to authenticate with */ - if(!data->state.aptr.user) + if(!Curl_creds_has_user(data->state.creds)) return CURLE_LOGIN_DENIED; /* Initialize the connection state */ @@ -487,20 +488,20 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; /* Parse the username, domain, and password */ - slash = strchr(conn->user, '/'); + slash = strchr(user, '/'); if(!slash) - slash = strchr(conn->user, '\\'); + slash = strchr(user, '\\'); if(slash) { smbc->user = slash + 1; - smbc->domain = curlx_strdup(conn->user); + smbc->domain = curlx_strdup(user); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; - smbc->domain[slash - conn->user] = 0; + smbc->domain[slash - user] = 0; } else { - smbc->user = conn->user; - smbc->domain = curlx_strdup(conn->host.name); + smbc->user = user; + smbc->domain = curlx_strdup(conn->origin->hostname); if(!smbc->domain) return CURLE_OUT_OF_MEMORY; } @@ -670,6 +671,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data) unsigned char nt_hash[21]; unsigned char nt[24]; size_t byte_count; + const char *passwd = Curl_creds_passwd(conn->creds); if(!smbc || !req) return CURLE_FAILED_INIT; @@ -680,9 +682,9 @@ static CURLcode smb_send_setup(struct Curl_easy *data) if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; - Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash); + Curl_ntlm_core_mk_lm_hash(passwd, lm_hash); Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm); - Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash); + Curl_ntlm_core_mk_nt_hash(passwd, nt_hash); Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt); memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes)); @@ -720,7 +722,8 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, struct smb_tree_connect msg; struct connectdata *conn = data->conn; char *p = msg.bytes; - const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) + + const size_t byte_count = strlen(conn->origin->hostname) + + strlen(smbc->share) + strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */ if(byte_count > sizeof(msg.bytes)) @@ -735,7 +738,7 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data, "\\\\%s\\" /* hostname */ "%s%c" /* share */ "%s", /* service */ - conn->host.name, smbc->share, 0, SERVICENAME); + conn->origin->hostname, 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); @@ -1004,8 +1007,8 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done) struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY); struct smb_header *h; enum smb_req_state next_state = SMB_DONE; - unsigned short len; - unsigned short off; + size_t len; + size_t off; CURLcode result; void *msg = NULL; const struct smb_nt_create_response *smb_m; @@ -1228,4 +1231,4 @@ const struct Curl_protocol Curl_protocol_smb = { ZERO_NULL, /* follow */ }; -#endif /* CURL_ENABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ +#endif /* CURL_ENABLE_SMB && USE_CURL_NTLM_CORE */ diff --git a/lib/smtp.c b/lib/smtp.c index b5c425cd7c18..2283f5e87d2d 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -182,7 +182,7 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn, static CURLcode smtp_parse_url_path(struct Curl_easy *data, struct smtp_conn *smtpc) { - /* The SMTP struct is already initialised in smtp_connect() */ + /* The SMTP struct is already initialized in smtp_connect() */ const char *path = &data->state.up.path[1]; /* skip leading path */ char localhost[HOSTNAME_MAX + 1]; @@ -347,7 +347,7 @@ static CURLcode cr_eob_read(struct Curl_easy *data, /* 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); + blen, (int)result, nread, eos); if(result) return result; @@ -432,7 +432,7 @@ static CURLcode cr_eob_read(struct Curl_easy *data, } *peos = (bool)ctx->eos; DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d", - blen, result, *pnread, *peos)); + blen, (int)result, *pnread, *peos)); return result; } @@ -608,7 +608,7 @@ static void smtp_state(struct Curl_easy *data, * * smtp_perform_ehlo() * - * Sends the EHLO command to not only initialise communication with the ESMTP + * Sends the EHLO command to not only initialize communication with the ESMTP * server but to also obtain a list of server side supported capabilities. */ static CURLcode smtp_perform_ehlo(struct Curl_easy *data, @@ -635,7 +635,7 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data, * * smtp_perform_helo() * - * Sends the HELO command to initialise communication with the SMTP server. + * Sends the HELO command to initialize communication with the SMTP server. */ static CURLcode smtp_perform_helo(struct Curl_easy *data, struct smtp_conn *smtpc) @@ -689,7 +689,8 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS); if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET); + result = Curl_ssl_cfilter_add( + data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET); if(result) goto out; /* Change the connection handler and SMTP state */ @@ -699,7 +700,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data, DEBUGASSERT(!smtpc->ssldone); result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone); DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d", - result, ssldone)); + (int)result, ssldone)); if(!result && ssldone) { smtpc->ssldone = ssldone; /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */ @@ -1677,7 +1678,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done) /* Initialize the SASL storage */ Curl_sasl_init(&smtpc->sasl, data, &saslsmtp); - /* Initialise the pingpong layer */ + /* Initialize the pingpong layer */ Curl_pp_init(&smtpc->pp, Curl_pgrs_now(data)); /* Parse the URL options */ @@ -1742,7 +1743,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status, /* Clear the transfer mode for the next request */ smtp->transfer = PPTRANSFER_BODY; CURL_TRC_SMTP(data, "smtp_done(status=%d, premature=%d) -> %d", - status, premature, result); + (int)status, premature, (int)result); return result; } @@ -1803,7 +1804,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, out: CURL_TRC_SMTP(data, "smtp_perform() -> %d, connected=%d, done=%d", - result, *connected, *dophase_done); + (int)result, *connected, *dophase_done); return result; } @@ -1852,7 +1853,7 @@ static CURLcode smtp_regular_transfer(struct Curl_easy *data, result = smtp_dophase_done(data, smtp, connected); CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d", - result, *dophase_done); + (int)result, *dophase_done); return result; } @@ -1884,7 +1885,7 @@ static CURLcode smtp_do(struct Curl_easy *data, bool *done) return result; result = smtp_regular_transfer(data, smtpc, smtp, done); - CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done); + CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", (int)result, *done); return result; } @@ -1935,7 +1936,8 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done) DEBUGF(infof(data, "DO phase is complete")); } - CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", result, *dophase_done); + CURL_TRC_SMTP(data, "smtp_doing() -> %d, done=%d", (int)result, + *dophase_done); return result; } @@ -1977,7 +1979,7 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; out: - CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result); + CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", (int)result); return result; } diff --git a/lib/sockaddr.h b/lib/sockaddr.h index 2b0333508763..916360d08954 100644 --- a/lib/sockaddr.h +++ b/lib/sockaddr.h @@ -40,4 +40,22 @@ struct Curl_sockaddr_storage { } buffer; }; +/* + * 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. + */ +struct Curl_sockaddr_ex { + int family; + int socktype; + int protocol; + unsigned int addrlen; + union { + struct sockaddr sa; + struct Curl_sockaddr_storage buf; + } addr; +}; + #endif /* HEADER_CURL_SOCKADDR_H */ diff --git a/lib/socketpair.c b/lib/socketpair.c index 76b959dd4afc..39a0e319b0f4 100644 --- a/lib/socketpair.c +++ b/lib/socketpair.c @@ -232,16 +232,9 @@ static int wakeup_inet(curl_socket_t socks[2], bool nonblocking) /* Do not block forever */ if(curlx_timediff_ms(curlx_now(), start) > (60 * 1000)) goto error; - if( -#ifdef USE_WINSOCK - /* 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) + if(SOCK_EAGAIN(sockerr) +#ifndef USE_WINSOCK + || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS) #endif ) { continue; @@ -309,7 +302,7 @@ int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking) int Curl_wakeup_signal(curl_socket_t socks[2]) { - int err = 0; + int sockerr = 0; #ifdef USE_EVENTFD const uint64_t buf[1] = { 1 }; #else @@ -317,22 +310,19 @@ int Curl_wakeup_signal(curl_socket_t socks[2]) #endif while(1) { - err = 0; + sockerr = 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) + sockerr = SOCKERRNO; +#ifndef USE_WINSOCK + if(sockerr == SOCKEINTR) continue; - if((err == SOCKEWOULDBLOCK) || (err == EAGAIN)) - err = 0; /* wakeup is already ongoing */ #endif + if(SOCK_EAGAIN(sockerr)) + sockerr = 0; /* wakeup is already ongoing */ } break; } - return err; + return sockerr; } CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) @@ -346,15 +336,13 @@ CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) if(!rc) break; else if(rc < 0) { -#ifdef USE_WINSOCK - if(SOCKERRNO == SOCKEWOULDBLOCK) - break; -#else - if(SOCKEINTR == SOCKERRNO) + int sockerr = SOCKERRNO; +#ifndef USE_WINSOCK + if(sockerr == SOCKEINTR) continue; - if((SOCKERRNO == SOCKEWOULDBLOCK) || (SOCKERRNO == EAGAIN)) - break; #endif + if(SOCK_EAGAIN(sockerr)) + break; result = CURLE_READ_ERROR; break; } diff --git a/lib/socketpair.h b/lib/socketpair.h index 0427e72fc53d..fd08c879dac6 100644 --- a/lib/socketpair.h +++ b/lib/socketpair.h @@ -27,7 +27,7 @@ #ifndef CURL_DISABLE_SOCKETPAIR -/* return < 0 for failure to initialise */ +/* return < 0 for failure to initialize */ int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking); void Curl_wakeup_destroy(curl_socket_t socks[2]); diff --git a/lib/socks.c b/lib/socks.c index b3da5be0a367..6c458c505d2f 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -94,13 +94,11 @@ static const char * const cf_socks_statename[] = { #define SOCKS_CHUNK_SIZE 1024 #define SOCKS_CHUNKS 1 - struct socks_ctx { enum socks_state_t state; struct bufq iobuf; - uint16_t remote_port; - const char *user; - const char *passwd; + struct Curl_peer *dest; + struct Curl_creds *creds; CURLproxycode presult; uint32_t resolv_id; uint8_t ip_version; @@ -109,7 +107,6 @@ struct socks_ctx { BIT(resolve_local); BIT(start_resolving); BIT(socks4a); - char hostname[1]; }; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -273,8 +270,8 @@ static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx, (void)data; buf[0] = 4; /* version (SOCKS4) */ buf[1] = 1; /* connect */ - buf[2] = (unsigned char)((sx->remote_port >> 8) & 0xffU); /* MSB */ - buf[3] = (unsigned char)(sx->remote_port & 0xffU); /* LSB */ + buf[2] = (unsigned char)((sx->dest->port >> 8) & 0xffU); /* MSB */ + buf[3] = (unsigned char)(sx->dest->port & 0xffU); /* LSB */ result = Curl_bufq_write(&sx->iobuf, buf, 4, &nwritten); if(result || (nwritten != 4)) @@ -288,8 +285,8 @@ static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, CURLcode result; size_t nwritten; - if(sx->user) { - size_t plen = strlen(sx->user); + if(sx->creds) { + size_t plen = strlen(sx->creds->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 @@ -298,7 +295,7 @@ static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, return CURLPX_LONG_USER; } /* add proxy name WITH trailing zero */ - result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1, + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, plen + 1, &nwritten); if(result || (nwritten != (plen + 1))) return CURLPX_SEND_REQUEST; @@ -329,7 +326,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, sx->start_resolving = FALSE; result = Curl_cf_dns_insert_after( cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + sx->dest, TRNSPRT_TCP, TRUE); if(result) { failf(data, "unable to create DNS filter for socks"); return CURLPX_UNKNOWN_FAIL; @@ -340,13 +337,13 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, result = Curl_conn_cf_connect(cf->next, data, &dns_done); if(result) { failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", - sx->hostname); + sx->dest->hostname); return CURLPX_RESOLVE_HOST; } else if(!dns_done) return CURLPX_OK; - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); if(ai) { struct sockaddr_in *saddr_in; char ipbuf[64]; @@ -365,7 +362,7 @@ static CURLproxycode socks4_resolving(struct socks_ctx *sx, } else { /* No ipv4 address resolved */ - failf(data, "SOCKS4 connection to %s not supported", sx->hostname); + failf(data, "SOCKS4 connection to %s not supported", sx->dest->hostname); return CURLPX_RESOLVE_HOST; } @@ -487,7 +484,8 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, /* SOCKS4 can only do IPv4, insist! */ sx->ip_version = CURL_IPRESOLVE_V4; CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u", - sx->socks4a ? "a" : "", sx->hostname, sx->remote_port); + sx->socks4a ? "a" : "", + sx->dest->hostname, sx->dest->port); /* * Compose socks4 request @@ -508,7 +506,7 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, /* 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 */ + size_t hlen = strlen(sx->dest->hostname) + 1; /* including NUL */ if(hlen > 255) { failf(data, "SOCKS4: too long hostname"); @@ -520,7 +518,8 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, 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); + result = Curl_bufq_cwrite(&sx->iobuf, sx->dest->hostname, hlen, + &nwritten); if(result || (nwritten != hlen)) return socks_failed(sx, cf, data, CURLPX_SEND_REQUEST); /* request complete */ @@ -554,7 +553,7 @@ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, FALLTHROUGH(); case SOCKS4_ST_RECV: - /* Receive 8 byte response */ + /* Receive 8-byte response */ presult = socks_recv(sx, cf, data, 8, &done); if(presult) return socks_failed(sx, cf, data, presult); @@ -591,7 +590,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, (void)cf; /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */ - if(!sx->resolve_local && strlen(sx->hostname) > 255) { + if(!sx->resolve_local && strlen(sx->dest->hostname) > 255) { failf(data, "SOCKS5: the destination hostname is too long to be " "resolved remotely by the proxy."); return CURLPX_LONG_HOSTNAME; @@ -602,7 +601,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, "CURLOPT_SOCKS5_AUTH: %u", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - sx->user = NULL; + Curl_creds_unlink(&sx->creds); req[0] = 5; /* version */ nauths = 1; @@ -613,7 +612,7 @@ static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, req[1 + nauths] = 1; /* GSS-API */ } #endif - if(sx->user) { + if(sx->creds) { ++nauths; req[1 + nauths] = 2; /* username/password */ } @@ -657,8 +656,7 @@ static CURLproxycode socks5_check_resp0(struct socks_ctx *sx, sxstate(sx, cf, data, SOCKS5_ST_GSSAPI_INIT); return CURLPX_OK; } - failf(data, - "SOCKS5 GSSAPI per-message authentication is not enabled."); + failf(data, "SOCKS5 GSSAPI per-message authentication is not enabled."); return CURLPX_GSSAPI_PERMSG; case 2: /* regular name + password authentication */ @@ -686,9 +684,9 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, unsigned char buf[2]; CURLcode result; - if(sx->user && sx->passwd) { - ulen = strlen(sx->user); - plen = strlen(sx->passwd); + if(sx->creds) { + ulen = strlen(sx->creds->user); + plen = strlen(sx->creds->passwd); /* the lengths must fit in a single byte */ if(ulen > 255) { failf(data, "Excessive username length for proxy auth"); @@ -713,7 +711,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; if(ulen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->user, ulen, &nwritten); if(result || (nwritten != ulen)) return CURLPX_SEND_REQUEST; } @@ -722,7 +720,7 @@ static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, if(result || (nwritten != 1)) return CURLPX_SEND_REQUEST; if(plen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->creds->passwd, plen, &nwritten); if(result || (nwritten != plen)) return CURLPX_SEND_REQUEST; } @@ -779,29 +777,29 @@ static CURLproxycode socks5_req1_init(struct socks_ctx *sx, /* remote resolving, send what type+addr/string to resolve */ #ifdef USE_IPV6 - if(strchr(sx->hostname, ':')) { + if(strchr(sx->dest->hostname, ':')) { desttype = 4; destination = ipbuf; destlen = 16; - if(curlx_inet_pton(AF_INET6, sx->hostname, ipbuf) != 1) + if(curlx_inet_pton(AF_INET6, sx->dest->hostname, ipbuf) != 1) return CURLPX_BAD_ADDRESS_TYPE; } else #endif - if(curlx_inet_pton(AF_INET, sx->hostname, ipbuf) == 1) { + if(curlx_inet_pton(AF_INET, sx->dest->hostname, ipbuf) == 1) { desttype = 1; destination = ipbuf; destlen = 4; } else { - const size_t hostname_len = strlen(sx->hostname); + const size_t hostname_len = strlen(sx->dest->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 */ + destination = (const unsigned char *)sx->dest->hostname; + destlen = (unsigned char)hostname_len; /* 1-byte length */ } req[3] = desttype; @@ -814,13 +812,13 @@ static CURLproxycode socks5_req1_init(struct socks_ctx *sx, 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); + req[0] = (unsigned char)((sx->dest->port >> 8) & 0xff); + req[1] = (unsigned char)(sx->dest->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); + sx->dest->hostname, sx->dest->port); return CURLPX_OK; } @@ -845,7 +843,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, sx->start_resolving = FALSE; result = Curl_cf_dns_insert_after( cf, data, Curl_resolv_dns_queries(data, sx->ip_version), - sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + sx->dest, TRNSPRT_TCP, TRUE); if(result) { failf(data, "unable to create DNS filter for socks"); return CURLPX_UNKNOWN_FAIL; @@ -855,7 +853,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, /* 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); + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", + sx->dest->hostname); return CURLPX_RESOLVE_HOST; } else if(!dns_done) @@ -863,13 +862,14 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, #ifdef USE_IPV6 if(data->set.ipver != CURL_IPRESOLVE_V4) - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET6, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET6, 0); #endif if(!ai) - ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + ai = Curl_cf_dns_get_ai(cf->next, data, sx->dest, AF_INET, 0); if(!ai) { - failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); + failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", + sx->dest->hostname); presult = CURLPX_RESOLVE_HOST; goto out; } @@ -883,7 +883,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, 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); + dest, sx->dest->port); } #ifdef USE_IPV6 else if(ai->ai_family == AF_INET6) { @@ -893,7 +893,7 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, 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); + dest, sx->dest->port); } #endif @@ -915,8 +915,8 @@ static CURLproxycode socks5_resolving(struct socks_ctx *sx, goto out; } /* PORT MSB+LSB */ - req[0] = (unsigned char)((sx->remote_port >> 8) & 0xffU); - req[1] = (unsigned char)(sx->remote_port & 0xffU); + req[0] = (unsigned char)((sx->dest->port >> 8) & 0xffU); + req[1] = (unsigned char)(sx->dest->port & 0xffU); result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) { presult = CURLPX_SEND_REQUEST; @@ -955,13 +955,13 @@ static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | +----+-----+-------+------+----------+----------+ - | 1 | 1 | X'00' | 1 | Variable | 2 | + | 1 | 1 | 0x00 | 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 + o IPv4 address: 0x01, BND.ADDR = 4-byte + o domain name: 0x03, BND.ADDR = [ 1-byte length, string ] + o IPv6 address: 0x04, BND.ADDR = 16-byte */ if(resp[0] != 5) { /* version */ failf(data, "SOCKS5 reply has wrong version, version should be 5."); @@ -971,7 +971,7 @@ static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, CURLproxycode rc = CURLPX_REPLY_UNASSIGNED; int code = resp[1]; failf(data, "cannot complete SOCKS5 connection to %s. (%d)", - sx->hostname, code); + sx->dest->hostname, code); if(code < 9) { /* RFC 1928 section 6 lists: */ static const CURLproxycode lookup[] = { @@ -1043,7 +1043,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, case SOCKS5_ST_START: CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u", - sx->hostname, sx->remote_port); + sx->dest->hostname, sx->dest->port); presult = socks5_req0_init(cf, sx, data); if(presult) return socks_failed(sx, cf, data, presult); @@ -1075,7 +1075,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, case SOCKS5_ST_GSSAPI_INIT: { #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* GSSAPI stuff done non-blocking */ - CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data); + CURLcode result = Curl_SOCKS5_gssapi_negotiate(cf, data, sx->creds); if(result) { failf(data, "Unable to negotiate SOCKS5 GSS-API context."); return CURLPX_GSSAPI; @@ -1083,8 +1083,7 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, sxstate(sx, cf, data, SOCKS5_ST_REQ1_INIT); goto process_state; #else - failf(data, - "SOCKS5 GSSAPI per-message authentication is not supported."); + failf(data, "SOCKS5 GSSAPI per-message authentication is not supported."); return socks_failed(sx, cf, data, CURLPX_GSSAPI_PERMSG); #endif } @@ -1181,6 +1180,8 @@ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, static void socks_proxy_ctx_free(struct socks_ctx *ctx) { if(ctx) { + Curl_peer_unlink(&ctx->dest); + Curl_creds_unlink(&ctx->creds); Curl_bufq_free(&ctx->iobuf); curlx_free(ctx); } @@ -1244,7 +1245,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, "(via %s port %u)", (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", ipquad.local_ip, ipquad.local_port, - ctx->hostname, ctx->remote_port, + ctx->dest->hostname, ctx->dest->port, ipquad.remote_ip, ipquad.remote_port); else infof(data, "Opened %sSOCKS connection", @@ -1255,10 +1256,8 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, out: *done = (bool)cf->connected; - if(*done || result) { - ctx->user = NULL; - ctx->passwd = NULL; - } + if(*done || result) + Curl_creds_unlink(&ctx->creds); return result; } @@ -1278,11 +1277,11 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, 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); + CURL_TRC_CF(data, cf, "adjust pollset out (%d)", (int)sx->state); result = Curl_pollset_set_out_only(data, ps, sock); break; default: - CURL_TRC_CF(data, cf, "adjust pollset in (%d)", sx->state); + CURL_TRC_CF(data, cf, "adjust pollset in (%d)", (int)sx->state); result = Curl_pollset_set_in_only(data, ps, sock); break; } @@ -1290,14 +1289,6 @@ static CURLcode socks_cf_adjust_pollset(struct Curl_cfilter *cf, return result; } -static void socks_proxy_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - cf->connected = FALSE; - if(cf->next) - cf->next->cft->do_close(cf->next, data); -} - static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -1315,8 +1306,8 @@ static CURLcode socks_cf_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_HOST_PORT: if(sx) { - *pres1 = sx->remote_port; - *((const char **)pres2) = sx->hostname; + *pres1 = sx->dest->port; + *((const char **)pres2) = sx->dest->hostname; return CURLE_OK; } break; @@ -1340,7 +1331,6 @@ struct Curl_cftype Curl_cft_socks_proxy = { 0, socks_proxy_cf_destroy, socks_proxy_cf_connect, - socks_proxy_cf_close, Curl_cf_def_shutdown, socks_cf_adjust_pollset, Curl_cf_def_data_pending, @@ -1354,19 +1344,16 @@ struct Curl_cftype Curl_cft_socks_proxy = { CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - const char *hostname, - uint16_t port, + struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, - const char *user, - const char *passwd) + struct Curl_creds *creds) { struct Curl_cfilter *cf; struct socks_ctx *ctx; - size_t hostlen = hostname ? strlen(hostname) : 0; CURLcode result; - if(!hostlen) + if(!dest) return CURLE_FAILED_INIT; switch(proxy_type) { @@ -1381,17 +1368,15 @@ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, } /* NUL byte already part of struct size */ - ctx = curlx_calloc(1, sizeof(*ctx) + hostlen); + ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { return CURLE_OUT_OF_MEMORY; } - memcpy(ctx->hostname, hostname, hostlen); - ctx->remote_port = port; + Curl_peer_link(&ctx->dest, dest); ctx->ip_version = ip_version; ctx->proxy_type = proxy_type; - ctx->user = user; - ctx->passwd = passwd; + Curl_creds_link(&ctx->creds, creds); Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, BUFQ_OPT_SOFT_LIMIT); diff --git a/lib/socks.h b/lib/socks.h index ea368326d2bf..d8e77c7f13a1 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -26,6 +26,10 @@ #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY + +struct Curl_peer; +struct Curl_creds; + /* * 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. @@ -43,22 +47,20 @@ CURLcode Curl_blockread_all(struct Curl_cfilter *cf, * This function handles the SOCKS5 GSS-API negotiation and initialization */ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_creds *creds); #endif -/* Insert a SOCKS filter after `cf_at` for connecting to `hostname` - * and `port` with optional credentials. - * Credentials are NOT duplicated and are +/* Insert a SOCKS filter after `cf_at` for connecting to `dest`. + * Credentials are optional and NOT duplicated and are * expected to exist during connect phase. */ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data, - const char *hostname, - uint16_t port, + struct Curl_peer *dest, uint8_t ip_version, uint8_t proxy_type, - const char *user, - const char *passwd); + struct Curl_creds *creds); extern struct Curl_cftype Curl_cft_socks_proxy; diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 32db07044a92..e10899c7e745 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -98,62 +98,33 @@ static int check_gss_err(struct Curl_easy *data, return 0; } -CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) +static CURLcode socks5_gss_create_service_name(struct Curl_easy *data, + struct connectdata *conn, + const char *serviceptr, + gss_name_t *server) { - struct connectdata *conn = cf->conn; - curl_socket_t sock = conn->sock[cf->sockindex]; - CURLcode code; - size_t actualread; - size_t nwritten; - CURLcode result; OM_uint32 gss_major_status, gss_minor_status, gss_status; - OM_uint32 gss_ret_flags; - int gss_conf_state, gss_enc; - gss_buffer_desc service = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; - gss_name_t server = GSS_C_NO_NAME; - gss_name_t gss_client_name = GSS_C_NO_NAME; - unsigned short us_length; - 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 - * +----+------+-----+----------------+ - * |VER | MTYP | LEN | TOKEN | - * +----+------+----------------------+ - * | 1 | 1 | 2 | up to 2^16 - 1 | - * +----+------+-----+----------------+ - */ + gss_buffer_desc service = GSS_C_EMPTY_BUFFER; /* prepare service name */ if(strchr(serviceptr, '/')) { - service.length = serviceptr_length; + service.length = strlen(serviceptr); 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 = curlx_malloc(serviceptr_length + - strlen(conn->socks_proxy.host.name) + 2); + service.value = curl_maprintf("%s@%s", serviceptr, + conn->socks_proxy.peer->hostname); if(!service.value) return CURLE_OUT_OF_MEMORY; - service.length = serviceptr_length + - strlen(conn->socks_proxy.host.name) + 1; - curl_msnprintf(service.value, service.length + 1, "%s@%s", - serviceptr, conn->socks_proxy.host.name); + service.length = strlen(service.value); gss_major_status = gss_import_name(&gss_minor_status, &service, - GSS_C_NT_HOSTBASED_SERVICE, &server); + GSS_C_NT_HOSTBASED_SERVICE, server); } curlx_safefree(service.value); @@ -162,25 +133,50 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_import_name()")) { failf(data, "Failed to create service name."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server); return CURLE_COULDNT_CONNECT; } - (void)curlx_nonblock(sock, FALSE); + return CURLE_OK; +} + +static CURLcode socks5_gss_auth_loop(struct Curl_cfilter *cf, + struct Curl_easy *data, + gss_name_t *server_ptr, + gss_ctx_id_t *gss_context, + OM_uint32 *gss_ret_flags) +{ + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc *gss_token = GSS_C_NO_BUFFER; + unsigned short us_length; + unsigned char socksreq[4]; + size_t actualread; + size_t nwritten; + CURLcode result; + + /* GSS-API request looks like + * +----+------+-----+----------------+ + * |VER | MTYP | LEN | TOKEN | + * +----+------+----------------------+ + * | 1 | 1 | 2 | up to 2^16 - 1 | + * +----+------+-----+----------------+ + */ /* 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, - &gss_context, - server, + gss_context, + *server_ptr, &Curl_krb5_mech_oid, NULL, gss_token, &gss_send_token, TRUE, - &gss_ret_flags); + gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) { curlx_safefree(gss_recv_token.value); @@ -190,10 +186,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, 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_name(&gss_status, server_ptr); 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."); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); + failf(data, "Failed to initialize GSS-API token."); return CURLE_COULDNT_CONNECT; } @@ -203,23 +199,24 @@ 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, socksreq, 4, FALSE, &nwritten); - if(code || (nwritten != 4)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, + &nwritten); + if(result || (nwritten != 4)) { failf(data, "Failed to send GSS-API authentication request."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); gss_release_buffer(&gss_status, &gss_send_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } - code = Curl_conn_cf_send(cf->next, data, - gss_send_token.value, - gss_send_token.length, FALSE, &nwritten); - if(code || (gss_send_token.length != nwritten)) { + result = Curl_conn_cf_send(cf->next, data, + gss_send_token.value, + gss_send_token.length, FALSE, &nwritten); + if(result || (gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); gss_release_buffer(&gss_status, &gss_send_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } } @@ -241,8 +238,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API authentication response."); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -250,16 +247,16 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 1) { /* status / message type */ failf(data, "Invalid GSS-API authentication response type (%d %d).", socksreq[0], socksreq[1]); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -268,8 +265,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, 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); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -279,8 +276,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Could not allocate memory for GSS-API authentication " "response token."); - gss_release_name(&gss_status, &server); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + gss_release_name(&gss_status, server_ptr); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } @@ -289,25 +286,34 @@ 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_name(&gss_status, server_ptr); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_token = &gss_recv_token; } - gss_release_name(&gss_status, &server); + gss_release_name(&gss_status, server_ptr); + return CURLE_OK; +} + +static CURLcode socks5_gss_auth_verify(struct Curl_easy *data, + gss_ctx_id_t *gss_context) +{ + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_name_t gss_client_name = GSS_C_NO_NAME; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; /* Everything is good so far, user was authenticated! */ - gss_major_status = gss_inquire_context(&gss_minor_status, gss_context, + gss_major_status = gss_inquire_context(&gss_minor_status, *gss_context, &gss_client_name, NULL, NULL, NULL, NULL, NULL, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_inquire_context")) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine username."); return CURLE_COULDNT_CONNECT; @@ -316,7 +322,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, &gss_send_token, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_display_name")) { - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + 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); failf(data, "Failed to determine username."); @@ -329,6 +335,26 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); + return CURLE_OK; +} + +static CURLcode socks5_gss_negotiate_enc(struct Curl_cfilter *cf, + struct Curl_easy *data, + gss_ctx_id_t *gss_context, + OM_uint32 gss_ret_flags) +{ + struct connectdata *conn = cf->conn; + OM_uint32 gss_major_status, gss_minor_status, gss_status; + gss_buffer_desc gss_send_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_recv_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc gss_w_token = GSS_C_EMPTY_BUFFER; + unsigned short us_length; + unsigned char socksreq[4]; + size_t actualread; + size_t nwritten; + CURLcode result; + int gss_enc; + /* Do encryption */ socksreq[0] = 1; /* GSS-API subnegotiation version */ socksreq[1] = 2; /* encryption message type */ @@ -383,19 +409,19 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_send_token.length = 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); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } - gss_major_status = gss_wrap(&gss_minor_status, gss_context, 0, + gss_major_status = gss_wrap(&gss_minor_status, *gss_context, 0, GSS_C_QOP_DEFAULT, &gss_send_token, - &gss_conf_state, &gss_w_token); + NULL, &gss_w_token); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { 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); + 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; } @@ -406,30 +432,30 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(socksreq + 2, &us_length, sizeof(short)); } - code = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); - if(code || (nwritten != 4)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 4, FALSE, &nwritten); + if(result || (nwritten != 4)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - code = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &nwritten); - if(code || (nwritten != 1)) { + result = Curl_conn_cf_send(cf->next, data, socksreq, 1, FALSE, &nwritten); + if(result || (nwritten != 1)) { failf(data, "Failed to send GSS-API encryption type."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { - 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)) { + result = Curl_conn_cf_send(cf->next, data, gss_w_token.value, + gss_w_token.length, FALSE, &nwritten); + if(result || (gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_w_token); @@ -438,7 +464,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API encryption response."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -446,14 +472,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / message type */ failf(data, "Invalid GSS-API encryption response type (%d %d).", socksreq[0], socksreq[1]); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -462,14 +488,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(!us_length) { failf(data, "Invalid zero-length GSS-API encryption token."); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_recv_token.length = us_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); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, @@ -479,20 +505,20 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Failed to receive GSS-API encryption type."); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(!data->set.socks5_gssapi_nec) { - gss_major_status = gss_unwrap(&gss_minor_status, gss_context, + gss_major_status = gss_unwrap(&gss_minor_status, *gss_context, &gss_recv_token, &gss_w_token, - 0, GSS_C_QOP_DEFAULT); + NULL, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { 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); + 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; } @@ -503,7 +529,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Invalid GSS-API encryption response length (%zu).", gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -516,7 +542,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_recv_token.length); curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -525,20 +551,48 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, 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] == 1) ? " GSS-API integrity" : - " GSS-API confidentiality")); + infof(data, "SOCKS5 access %s protection granted.", + (socksreq[0] == 0) ? "without GSS-API data" : + ((socksreq[0] == 1) ? "with GSS-API integrity" : + "with GSS-API confidentiality")); conn->socks5_gssapi_enctype = socksreq[0]; if(socksreq[0] == 0) - Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, gss_context, NULL); return CURLE_OK; } +CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_creds *creds) +{ + struct connectdata *conn = cf->conn; + curl_socket_t sock = conn->sock[cf->sockindex]; + CURLcode result; + OM_uint32 gss_ret_flags = 0; + gss_name_t server = GSS_C_NO_NAME; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : "rcmd"; + gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT; + + result = socks5_gss_create_service_name(data, conn, service, &server); + if(!result) { + (void)curlx_nonblock(sock, FALSE); + result = socks5_gss_auth_loop(cf, data, &server, &gss_context, + &gss_ret_flags); + } + if(!result) + result = socks5_gss_auth_verify(data, &gss_context); + if(!result) + result = socks5_gss_negotiate_enc(cf, data, &gss_context, gss_ret_flags); + + /* unconditionally put it back to non-blocking */ + (void)curlx_nonblock(sock, TRUE); + + return result; +} + #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 385312a3681c..10a8be1773ae 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -58,12 +58,13 @@ static int check_sspi_err(struct Curl_easy *data, /* This is the SSPI-using version of this function */ static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, struct Curl_easy *data, + struct Curl_creds *creds, CredHandle *cred_handle, char **service_namep) { struct connectdata *conn = cf->conn; - const char *service = data->set.str[STRING_PROXY_SERVICE_NAME] ? - data->set.str[STRING_PROXY_SERVICE_NAME] : "rcmd"; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : "rcmd"; SECURITY_STATUS status; /* prepare service name */ @@ -71,7 +72,7 @@ static CURLcode socks5_sspi_setup(struct Curl_cfilter *cf, *service_namep = curlx_strdup(service); else *service_namep = curl_maprintf("%s/%s", - service, conn->socks_proxy.host.name); + service, conn->socks_proxy.peer->hostname); if(!*service_namep) return CURLE_OUT_OF_MEMORY; @@ -163,7 +164,7 @@ static CURLcode socks5_sspi_loop(struct Curl_cfilter *cf, sspi_recv_token.cbBuffer = 0; if(check_sspi_err(data, status, "InitializeSecurityContext")) { - failf(data, "Failed to initialise security context."); + failf(data, "Failed to initialize security context."); return socks5_free_token(&sspi_send_token, CURLE_COULDNT_CONNECT); } @@ -420,8 +421,7 @@ static CURLcode socks5_sspi_encrypt(struct Curl_cfilter *cf, if(result || (actualread != us_length)) { failf(data, "Failed to receive SSPI encryption type."); - curlx_free(sspi_w_token[0].pvBuffer); - return result ? result : CURLE_COULDNT_CONNECT; + goto fail; } if(!data->set.socks5_gssapi_nec) { @@ -431,49 +431,48 @@ static CURLcode socks5_sspi_encrypt(struct Curl_cfilter *cf, sspi_w_token[1].cbBuffer = 0; sspi_w_token[1].pvBuffer = NULL; - status = Curl_pSecFn->DecryptMessage(sspi_context, &wrap_desc, - 0, &qop); + /* At least one of the descriptors must be of type SECBUFFER_DATA. The + message is decrypted in place so the SECBUFFER_DATA receives a pointer + to the message in SECBUFFER_STREAM. */ + status = Curl_pSecFn->DecryptMessage(sspi_context, &wrap_desc, 0, &qop); - if(check_sspi_err(data, status, "DecryptMessage")) { - 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(check_sspi_err(data, status, "DecryptMessage")) + goto fail; if(sspi_w_token[1].cbBuffer != 1) { failf(data, "Invalid SSPI encryption response length (%lu).", (unsigned long)sspi_w_token[1].cbBuffer); - if(sspi_w_token[1].pvBuffer) - Curl_pSecFn->FreeContextBuffer(sspi_w_token[1].pvBuffer); - curlx_free(sspi_w_token[0].pvBuffer); - return CURLE_COULDNT_CONNECT; + goto fail; } memcpy(socksreq, sspi_w_token[1].pvBuffer, sspi_w_token[1].cbBuffer); - 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); - curlx_free(sspi_w_token[0].pvBuffer); - return CURLE_COULDNT_CONNECT; + goto fail; } memcpy(socksreq, sspi_w_token[0].pvBuffer, sspi_w_token[0].cbBuffer); } curlx_free(sspi_w_token[0].pvBuffer); - infof(data, "SOCKS5 access with%s protection granted BUT NOT USED.", - (socksreq[0] == 0) ? "out GSS-API data" : - ((socksreq[0] == 1) ? " GSS-API integrity" : - " GSS-API confidentiality")); + infof(data, "SOCKS5 access %s protection granted BUT NOT USED.", + (socksreq[0] == 0) ? "without GSS-API data" : + ((socksreq[0] == 1) ? "with GSS-API integrity" : + "with GSS-API confidentiality")); + cf->conn->socks5_gssapi_enctype = socksreq[0]; return CURLE_OK; + +fail: + curlx_free(sspi_w_token[0].pvBuffer); + return CURLE_COULDNT_CONNECT; } CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_creds *creds) { struct connectdata *conn = cf->conn; curl_socket_t sock = conn->sock[cf->sockindex]; @@ -489,7 +488,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memset(&sspi_context, 0, sizeof(sspi_context)); names.sUserName = NULL; - result = socks5_sspi_setup(cf, data, &cred_handle, &service_name); + result = socks5_sspi_setup(cf, data, creds, &cred_handle, &service_name); if(result) goto error; diff --git a/lib/strerror.c b/lib/strerror.c index 1e97c2829db5..7b6dc0212788 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -172,7 +172,7 @@ const char *curl_easy_strerror(CURLcode error) return "Can not set SSL crypto engine as default"; case CURLE_SSL_ENGINE_INITFAILED: - return "Failed to initialise SSL crypto engine"; + return "Failed to initialize SSL crypto engine"; case CURLE_SEND_ERROR: return "Failed sending data to the peer"; @@ -647,14 +647,15 @@ const char *Curl_sspi_strerror(SECURITY_STATUS err, char *buf, size_t buflen) "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); + "the Windows System event log.", (unsigned long)err); } else { char msgbuf[256]; if(curlx_get_winapi_error((DWORD)err, msgbuf, sizeof(msgbuf))) - curl_msnprintf(buf, buflen, "%s (0x%08lx) - %s", txt, err, msgbuf); + curl_msnprintf(buf, buflen, "%s (0x%08lx) - %s", txt, (unsigned long)err, + msgbuf); else - curl_msnprintf(buf, buflen, "%s (0x%08lx)", txt, err); + curl_msnprintf(buf, buflen, "%s (0x%08lx)", txt, (unsigned long)err); } #else /* CURLVERBOSE */ if(err == SEC_E_OK) diff --git a/lib/telnet.c b/lib/telnet.c index c5ce9c2c97e3..2870d7e2c9c0 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -53,6 +53,7 @@ #include "curl_trc.h" #include "progress.h" #include "arpa_telnet.h" +#include "connect.h" #include "select.h" #include "curlx/strparse.h" @@ -74,8 +75,8 @@ #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) */ +#define CURL_SB_PEEK(x) (*(x)->subpointer & 0xff) +#define CURL_SB_EOF(x) ((x)->subpointer >= (x)->subend) */ /* For negotiation compliant to RFC 1143 */ #define CURL_NO 0 @@ -195,20 +196,15 @@ static CURLcode init_telnet(struct Curl_easy *data) 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 - */ + /* To be compliant with previous releases of libcurl we enable this option + by default. This behavior can be changed with 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 - */ + /* We must allow the server to echo what we sent but it is not necessary + to request the server to do so (it might force 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 @@ -216,12 +212,11 @@ static CURLcode init_telnet(struct Curl_easy *data) 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). */ + 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); @@ -238,10 +233,8 @@ static void send_negotiation(struct Curl_easy *data, int cmd, int option) buf[2] = (unsigned char)option; bytes_written = swrite(conn->sock[FIRSTSOCKET], buf, 3); - if(bytes_written < 0) { - int err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); - } + if(bytes_written < 0) + failf(data, "Sending data failed (%d)", SOCKERRNO); printoption(data, "SENT", cmd, option); } @@ -645,13 +638,19 @@ static CURLcode send_telnet_data(struct Curl_easy *data, while(!result && total_written < outlen) { /* Make sure socket is writable to avoid EWOULDBLOCK condition */ struct pollfd pfd[1]; + timediff_t timeout_ms = Curl_timeleft_ms(data); pfd[0].fd = conn->sock[FIRSTSOCKET]; pfd[0].events = POLLOUT; - switch(Curl_poll(pfd, 1, -1)) { + if(timeout_ms < 0) + return CURLE_OPERATION_TIMEDOUT; + /* 0 means no timeout configured; pass -1 to poll for infinite wait */ + switch(Curl_poll(pfd, 1, timeout_ms ? timeout_ms : -1)) { case -1: /* error, abort writing */ - case 0: /* timeout (will never happen) */ result = CURLE_SEND_ERROR; break; + case 0: /* timeout */ + result = CURLE_OPERATION_TIMEDOUT; + break; default: /* write! */ bytes_written = 0; result = Curl_xfer_send(data, outbuf + total_written, @@ -673,7 +672,6 @@ 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; @@ -706,19 +704,15 @@ static void sendsuboption(struct Curl_easy *data, /* 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); - } + if(bytes_written < 0) + failf(data, "Sending data failed (%d)", SOCKERRNO); /* ... 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); - } + if(bytes_written < 0) + failf(data, "Sending data failed (%d)", SOCKERRNO); break; } } @@ -773,7 +767,7 @@ static void rec_do(struct Curl_easy *data, struct TELNET *tn, int option) break; case CURL_OPPOSITE: tn->us[option] = CURL_WANTNO; - tn->himq[option] = CURL_EMPTY; + tn->usq[option] = CURL_EMPTY; send_negotiation(data, CURL_WONT, option); break; } @@ -840,13 +834,14 @@ static CURLcode check_telnet_options(struct Curl_easy *data, /* Add the username as an environment variable if it was given on the command line */ - if(data->state.aptr.user) { + if(data->state.creds) { char buffer[256]; - if(str_is_nonascii(data->conn->user)) { + if(str_is_nonascii(Curl_creds_user(data->conn->creds))) { DEBUGF(infof(data, "set a non ASCII username in telnet")); return CURLE_BAD_FUNCTION_ARGUMENT; } - curl_msnprintf(buffer, sizeof(buffer), "USER,%s", data->conn->user); + curl_msnprintf(buffer, sizeof(buffer), "USER,%s", + Curl_creds_user(data->conn->creds)); beg = curl_slist_append(tn->telnet_vars, buffer); if(!beg) { curl_slist_free_all(tn->telnet_vars); @@ -979,7 +974,6 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) unsigned char temp[2048]; ssize_t bytes_written; size_t len; - int err; struct connectdata *conn = data->conn; if(!CURL_SB_LEN(tn)) /* ignore empty suboption */ @@ -992,7 +986,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) if(bad_option(tn->subopt_ttype)) return CURLE_BAD_FUNCTION_ARGUMENT; if(strlen(tn->subopt_ttype) > 1000) { - failf(data, "Tool long telnet TTYPE"); + failf(data, "Too long telnet TTYPE"); return CURLE_SEND_ERROR; } len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", @@ -1002,8 +996,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); + failf(data, "Sending data failed (%d)", SOCKERRNO); return CURLE_SEND_ERROR; } printsub(data, '>', &temp[2], len-2); @@ -1012,7 +1005,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) if(bad_option(tn->subopt_xdisploc)) return CURLE_BAD_FUNCTION_ARGUMENT; if(strlen(tn->subopt_xdisploc) > 1000) { - failf(data, "Tool long telnet XDISPLOC"); + failf(data, "Too long telnet XDISPLOC"); return CURLE_SEND_ERROR; } len = curl_msnprintf((char *)temp, sizeof(temp), "%c%c%c%c%s%c%c", @@ -1021,8 +1014,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) CURL_SE); bytes_written = swrite(conn->sock[FIRSTSOCKET], temp, len); if(bytes_written < 0) { - err = SOCKERRNO; - failf(data, "Sending data failed (%d)", err); + failf(data, "Sending data failed (%d)", SOCKERRNO); return CURLE_SEND_ERROR; } printsub(data, '>', &temp[2], len - 2); @@ -1036,7 +1028,7 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) if(bad_option(v->data)) return CURLE_BAD_FUNCTION_ARGUMENT; /* Add the variable if it fits */ - if(len + tmplen < (int)sizeof(temp) - 6) { + if(len + tmplen < sizeof(temp) - 6) { const char *s = strchr(v->data, ','); if(!s) len += curl_msnprintf((char *)&temp[len], sizeof(temp) - len, @@ -1053,10 +1045,8 @@ static CURLcode suboption(struct Curl_easy *data, struct TELNET *tn) "%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); - } + if(bytes_written < 0) + failf(data, "Sending data failed (%d)", SOCKERRNO); printsub(data, '>', &temp[2], len - 2); break; } @@ -1239,7 +1229,6 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) DWORD obj_count; DWORD wait_timeout; DWORD readfile_read; - int err; #else timediff_t interval_ms; struct pollfd pfd[2]; @@ -1283,7 +1272,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) return CURLE_RECV_ERROR; } - /* The get the Windows file handle for stdin */ + /* Then get the Windows file handle for stdin */ stdin_handle = GetStdHandle(STD_INPUT_HANDLE); /* Create the list of objects to wait for */ @@ -1377,9 +1366,9 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) case WAIT_OBJECT_0: { events.lNetworkEvents = 0; if(WSAEnumNetworkEvents(sockfd, event_handle, &events) != 0) { - err = SOCKERRNO; - if(err != SOCKEINPROGRESS) { - infof(data, "WSAEnumNetworkEvents failed (%d)", err); + int sockerr = SOCKERRNO; + if(sockerr != SOCKEINPROGRESS) { + infof(data, "WSAEnumNetworkEvents failed (%d)", sockerr); keepon = FALSE; result = CURLE_READ_ERROR; } diff --git a/lib/tftp.c b/lib/tftp.c index 6cc672d447bf..00e89752bddc 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -167,7 +167,8 @@ static CURLcode tftp_set_timeouts(struct tftp_conn *state) } /* Set per-block timeout to total */ - if(timeout_ms > 0) + if((timeout_ms > 0) && (timeout_ms < 3600000)) + /* do the calculation only if the timeout is "reasonable" */ timeout = (time_t)(timeout_ms + 500) / 1000; else timeout = 15; @@ -266,16 +267,19 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, while(tmp < ptr + len) { const char *option, *value; + size_t olen; tmp = tftp_option_get(tmp, ptr + len - tmp, &option, &value); if(!tmp) { failf(data, "Malformed ACK packet, rejecting"); return CURLE_TFTP_ILLEGAL; } + olen = strlen(option); infof(data, "got option=(%s) value=(%s)", option, value); - if(checkprefix(TFTP_OPTION_BLKSIZE, option)) { + if((strlen(TFTP_OPTION_BLKSIZE) == olen) && + checkprefix(TFTP_OPTION_BLKSIZE, option)) { curl_off_t blksize; if(curlx_str_number(&value, &blksize, TFTP_BLKSIZE_MAX)) { failf(data, "%s (%d)", "blksize is larger than max supported", @@ -304,7 +308,8 @@ static CURLcode tftp_parse_option_ack(struct tftp_conn *state, infof(data, "blksize parsed from OACK (%u) requested (%u)", state->blksize, state->requested_blksize); } - else if(checkprefix(TFTP_OPTION_TSIZE, option)) { + else if((strlen(TFTP_OPTION_TSIZE) == olen) && + checkprefix(TFTP_OPTION_TSIZE, option)) { curl_off_t tsize = 0; /* tsize should be ignored on upload: Who cares about the size of the remote file? */ @@ -486,7 +491,7 @@ static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event) break; default: - failf(data, "tftp_tx: internal error, event: %i", (int)event); + failf(data, "tftp_tx: internal error, event: %d", (int)event); break; } @@ -671,7 +676,7 @@ static CURLcode tftp_send_first(struct tftp_conn *state, } if(data->state.upload) { - /* If we are uploading, send an WRQ */ + /* If we are uploading, send a WRQ */ setpacketevent(&state->spacket, TFTP_EVENT_WRQ); if(data->state.infilesize != -1) Curl_pgrsSetUploadSize(data, data->state.infilesize); @@ -736,7 +741,7 @@ static CURLcode tftp_send_first(struct tftp_conn *state, } } - /* the typecase for the 3rd argument is mostly for systems that do + /* the typecast for the 3rd argument is mostly for systems that do not have a size_t argument, like older unixes that want an 'int' */ #ifdef __AMIGA__ #define CURL_SENDTO_ARG5(x) CURL_UNCONST(x) @@ -869,7 +874,7 @@ static CURLcode tftp_state_machine(struct tftp_conn *state, infof(data, "%s", "TFTP finished"); break; default: - DEBUGF(infof(data, "STATE: %d", state->state)); + DEBUGF(infof(data, "STATE: %d", (int)state->state)); failf(data, "%s", "Internal state machine error"); result = CURLE_TFTP_ILLEGAL; break; @@ -1192,9 +1197,9 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) if(rc == -1) { /* bail out */ - int error = SOCKERRNO; + int sockerr = SOCKERRNO; char buffer[STRERROR_LEN]; - failf(data, "%s", curlx_strerror(error, buffer, sizeof(buffer))); + failf(data, "%s", curlx_strerror(sockerr, buffer, sizeof(buffer))); state->event = TFTP_EVENT_ERROR; } else if(rc) { diff --git a/lib/thrdpool.c b/lib/thrdpool.c index 22faa396ed66..e8c7d3643551 100644 --- a/lib/thrdpool.c +++ b/lib/thrdpool.c @@ -130,9 +130,9 @@ static CURL_THREAD_RETURN_T CURL_STDCALL thrdslot_run(void *arg) * 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) && + CURLcode result = Curl_cond_timedwait(&tslot->await, &tpool->lock, + tpool->idle_time_ms); + if((result == CURLE_OPERATION_TIMEDOUT) && (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { goto out; } diff --git a/lib/thrdqueue.c b/lib/thrdqueue.c index 1521ccfbee10..f68f8e17973c 100644 --- a/lib/thrdqueue.c +++ b/lib/thrdqueue.c @@ -351,11 +351,16 @@ void Curl_thrdq_clear(struct curl_thrdq *tqueue, Curl_mutex_release(&tqueue->lock); } -CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, - uint32_t timeout_ms) +#ifdef UNITTESTS +/* @unittest 3301 */ +UNITTEST CURLcode thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms); +UNITTEST CURLcode thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms) { return Curl_thrdpool_await_idle(tqueue->tpool, timeout_ms); } +#endif CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, uint32_t max_len, diff --git a/lib/thrdqueue.h b/lib/thrdqueue.h index d267f5d09105..1144bb4185e8 100644 --- a/lib/thrdqueue.h +++ b/lib/thrdqueue.h @@ -26,23 +26,24 @@ #include "curl_setup.h" #include "curlx/timediff.h" +struct curl_thrdq; + #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. */ +/* Notification callback when "events" happen in the queue. May be called 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. */ +/* Process a queued item. May be called 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 diff --git a/lib/transfer.c b/lib/transfer.c index fd1a903dabc8..1a4138f6c156 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -332,7 +332,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, out: Curl_multi_xfer_buf_release(data, xfer_buf); if(result) - DEBUGF(infof(data, "sendrecv_dl() -> %d", result)); + DEBUGF(infof(data, "sendrecv_dl() -> %d", (int)result)); return result; } @@ -425,7 +425,7 @@ CURLcode Curl_sendrecv(struct Curl_easy *data) out: if(result) - DEBUGF(infof(data, "Curl_sendrecv() -> %d", result)); + DEBUGF(infof(data, "Curl_sendrecv() -> %d", (int)result)); return result; } @@ -438,40 +438,6 @@ 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 @@ -524,6 +490,10 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) #endif data->state.httpreq = data->set.method; + /* initial transfer request coming up, forget the initial origin + * from a previous perform() on this handle. */ + Curl_peer_unlink(&data->state.initial_origin); + Curl_peer_unlink(&data->state.origin); data->state.requests = 0; data->state.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ @@ -542,6 +512,8 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) if(data->set.str[STRING_SET_REFERER]) Curl_bufref_set(&data->state.referer, data->set.str[STRING_SET_REFERER], 0, NULL); + else + Curl_bufref_free(&data->state.referer); if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; @@ -625,11 +597,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } - if(!result) - result = Curl_reset_userpwd(data); - if(!result) - result = Curl_reset_proxypwd(data); - data->req.headerbytecount = 0; Curl_headers_cleanup(data); return result; @@ -712,7 +679,7 @@ static void xfer_setup( struct SingleRequest *k = &data->req; struct connectdata *conn = data->conn; - DEBUGASSERT(conn != NULL); + DEBUGASSERT(conn); /* indexes are in range */ DEBUGASSERT((send_idx <= 1) && (send_idx >= -1)); DEBUGASSERT((recv_idx <= 1) && (recv_idx >= -1)); @@ -811,7 +778,7 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data, data->req.download_done = TRUE; } CURL_TRC_WRITE(data, "xfer_write_resp(len=%zu, eos=%d) -> %d", - blen, is_eos, result); + blen, is_eos, (int)result); return result; } @@ -868,7 +835,7 @@ CURLcode Curl_xfer_send(struct Curl_easy *data, data->info.request_size += *pnwritten; DEBUGF(infof(data, "Curl_xfer_send(len=%zu, eos=%d) -> %d, %zu", - blen, eos, result, *pnwritten)); + blen, eos, (int)result, *pnwritten)); return result; } @@ -936,22 +903,14 @@ CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable) bool Curl_xfer_is_secure(struct Curl_easy *data) { - 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; - } - 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 */ +#ifndef CURL_DISABLE_PROXY + if(data->conn && data->conn->bits.origin_is_proxy) { + /* talking to a forward proxy, not secure. we do not use + * a forward proxy for https: and other 's' URLs. Let's just check that + * this did not fail somewhere. */ + DEBUGASSERT(!(data->state.origin->scheme->flags & PROTOPT_SSL)); + return FALSE; } - return scheme ? (scheme->flags & PROTOPT_SSL) : FALSE; +#endif + return (data->state.origin->scheme->flags & PROTOPT_SSL); } diff --git a/lib/transfer.h b/lib/transfer.h index b29e70b9ec12..7507ce27bdb9 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -31,8 +31,6 @@ char *Curl_checkheaders(const struct Curl_easy *data, 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); @@ -145,8 +143,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); -/* TRUE if the transfer is secure (e.g. TLS) from libcurl to the - * URL's host. */ +/* TRUE if the transfer is secure, e.g. uses TLS and does not + * use a forward proxy. */ 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 5469174944d9..55aedb234b05 100644 --- a/lib/uint-bset.c +++ b/lib/uint-bset.c @@ -157,7 +157,7 @@ bool Curl_uint32_bset_next(struct uint32_bset *bset, uint32_t last, /* shift away the bits we already iterated in this slot */ x = (bset->slots[islot] >> (last % 64)); if(x) { - /* more bits set, next is `last` + trailing0s of the shifted slot */ + /* more bits set, next is `last` + trailing 0s of the shifted slot */ *pnext = last + CURL_CTZ64(x); return TRUE; } @@ -179,10 +179,10 @@ 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 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 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 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 */ diff --git a/lib/uint-spbset.c b/lib/uint-spbset.c index 3daa2eea758e..d4e6c8e70bf5 100644 --- a/lib/uint-spbset.c +++ b/lib/uint-spbset.c @@ -203,7 +203,7 @@ static bool uint32_spbset_chunk_next(struct uint32_spbset_chunk *chunk, 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 */ + /* more bits set, next is `last` + trailing 0s of the shifted slot */ *pnext = last + CURL_CTZ64(x); return TRUE; } diff --git a/lib/url.c b/lib/url.c index 5fe68033d9d6..505e08a7e10a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -86,7 +86,7 @@ #include "urlapi-int.h" #include "system_win32.h" #include "hsts.h" -#include "noproxy.h" +#include "proxy.h" #include "cfilters.h" #include "idn.h" #include "http_proxy.h" @@ -99,6 +99,7 @@ #include "headers.h" #include "curlx/strerr.h" #include "curlx/strparse.h" +#include "peer.h" /* Now for the protocols */ #include "ftp.h" @@ -106,6 +107,7 @@ #include "telnet.h" #include "tftp.h" #include "http.h" +#include "vauth/vauth.h" #include "file.h" #include "curl_ldap.h" #include "vssh/ssh.h" @@ -118,12 +120,6 @@ #include "smtp.h" #include "ws.h" -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data); -#else -#define data_priority_cleanup(x) -#endif - /* 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. @@ -132,9 +128,6 @@ static void data_priority_cleanup(struct Curl_easy *data); # error READBUFFER_SIZE is too small #endif -/* Reject URLs exceeding this length */ -#define MAX_URL_LEN 0xffff - /* * get_protocol_family() * @@ -252,7 +245,8 @@ CURLcode Curl_close(struct Curl_easy **datap) /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); - curlx_safefree(data->state.first_host); + Curl_peer_unlink(&data->state.origin); + Curl_peer_unlink(&data->state.initial_origin); Curl_ssl_free_certinfo(data); Curl_bufref_free(&data->state.referer); @@ -277,13 +271,12 @@ CURLcode Curl_close(struct Curl_easy **datap) curlx_safefree(data->info.contenttype); curlx_safefree(data->info.wouldredirect); - data_priority_cleanup(data); - /* No longer a dirty share, if it exists */ if(Curl_share_easy_unlink(data)) DEBUGASSERT(0); Curl_hash_destroy(&data->meta_hash); + Curl_creds_unlink(&data->state.creds); curlx_safefree(data->state.aptr.uagent); curlx_safefree(data->state.aptr.accept_encoding); curlx_safefree(data->state.aptr.rangeline); @@ -295,12 +288,6 @@ CURLcode Curl_close(struct Curl_easy **datap) #ifndef CURL_DISABLE_RTSP curlx_safefree(data->state.aptr.rtsp_transport); #endif - curlx_safefree(data->state.aptr.user); - curlx_safefree(data->state.aptr.passwd); -#ifndef CURL_DISABLE_PROXY - 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); @@ -312,6 +299,13 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_freeset(data); Curl_headers_cleanup(data); Curl_netrc_cleanup(&data->state.netrc); +#ifndef CURL_DISABLE_DIGEST_AUTH + curlx_free(data->state.envproxy); +#endif + Curl_ssl_config_cleanup(&data->set.ssl.primary); +#ifndef CURL_DISABLE_PROXY + Curl_ssl_config_cleanup(&data->set.proxy_ssl.primary); +#endif curlx_free(data); return CURLE_OK; } @@ -366,7 +360,9 @@ void Curl_init_userdefined(struct Curl_easy *data) set->httpauth = CURLAUTH_BASIC; /* defaults to basic */ + Curl_ssl_config_init(&data->set.ssl.primary); #ifndef CURL_DISABLE_PROXY + Curl_ssl_config_init(&data->set.proxy_ssl.primary); set->proxyport = 0; set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */ @@ -374,7 +370,6 @@ void Curl_init_userdefined(struct Curl_easy *data) set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; #endif - Curl_ssl_easy_config_init(data); #ifndef CURL_DISABLE_DOH set->doh_verifyhost = TRUE; set->doh_verifypeer = TRUE; @@ -448,7 +443,7 @@ static void easy_meta_freeentry(void *p) /* 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); + DEBUGASSERT(!p); } /** @@ -513,34 +508,24 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_conn_cf_discard_all(data, conn, (int)i); } - Curl_free_idnconverted_hostname(&conn->host); - Curl_free_idnconverted_hostname(&conn->conn_to_host); #ifndef CURL_DISABLE_PROXY - Curl_free_idnconverted_hostname(&conn->http_proxy.host); - Curl_free_idnconverted_hostname(&conn->socks_proxy.host); - 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 - curlx_safefree(conn->user); - curlx_safefree(conn->passwd); - curlx_safefree(conn->sasl_authzid); + Curl_peer_unlink(&conn->http_proxy.peer); + Curl_peer_unlink(&conn->socks_proxy.peer); + Curl_creds_unlink(&conn->http_proxy.creds); + Curl_creds_unlink(&conn->socks_proxy.creds); +#endif + Curl_creds_unlink(&conn->creds); + Curl_peer_unlink(&conn->creds_origin); 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 - curlx_safefree(conn->unix_domain_socket); -#endif curlx_safefree(conn->destination); Curl_hash_destroy(&conn->meta_hash); + Curl_peer_unlink(&conn->origin); + Curl_peer_unlink(&conn->via_peer); + Curl_peer_unlink(&conn->origin2); + Curl_peer_unlink(&conn->via_peer2); curlx_free(conn); /* free all the connection oriented data */ } @@ -576,12 +561,8 @@ 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)) { - - if(Curl_timestrcmp(data->user, needle->user) || - Curl_timestrcmp(data->passwd, needle->passwd)) - return FALSE; + Curl_peer_same_destination(data->peer, needle->peer) && + Curl_creds_same(data->creds, needle->creds)) { return TRUE; } return FALSE; @@ -651,7 +632,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn, Curl_attach_connection(data, conn); dead = !Curl_conn_is_alive(data, conn, &input_pending); if(input_pending) { - /* For reuse, we want a "clean" connection state. The includes + /* For reuse, we want a "clean" connection state. This includes * that we expect - in general - no waiting input data. Input * waiting might be a TLS Notify Close, for example. We reject * that. @@ -714,7 +695,11 @@ struct url_conn_match { 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(may_tls); /* May upgrade clear-text connection to TLS, can only reuse + * connections that have matching TLS configuration. + * Always TRUE if `req_tls` is TRUE. */ + BIT(require_tls); /* Requires TLS use from a clear-text start, can only + * reuse connections that have TLS. */ BIT(wait_pipe); BIT(force_reuse); BIT(seen_pending_conn); @@ -753,30 +738,11 @@ static bool url_match_connect_config(struct connectdata *conn, return FALSE; } - if(m->needle->bits.conn_to_host != conn->bits.conn_to_host) + if(!m->needle->via_peer != !conn->via_peer) /* do not mix connections that use the "connect to host" feature and * connections that do not use this feature */ return FALSE; - if(m->needle->bits.conn_to_port != conn->bits.conn_to_port) - /* do not mix connections that use the "connect to port" feature and - * connections that do not use this feature */ - return FALSE; - - /* Does `conn` use the correct protocol? */ -#ifdef USE_UNIX_SOCKETS - if(m->needle->unix_domain_socket) { - if(!conn->unix_domain_socket) - return FALSE; - if(strcmp(m->needle->unix_domain_socket, conn->unix_domain_socket)) - return FALSE; - if(m->needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket) - return FALSE; - } - else if(conn->unix_domain_socket) - return FALSE; -#endif - return TRUE; } @@ -866,7 +832,7 @@ static bool url_match_ssl_use(struct connectdata *conn, (get_protocol_family(conn->scheme) != m->needle->scheme->protocol)) return FALSE; } - else if(m->req_tls) + else if(m->require_tls) /* a clear-text STARTTLS protocol with required TLS */ return FALSE; return TRUE; @@ -876,36 +842,27 @@ static bool url_match_ssl_use(struct connectdata *conn, static bool url_match_proxy_use(struct connectdata *conn, struct url_conn_match *m) { - if(m->needle->bits.httpproxy != conn->bits.httpproxy || - m->needle->bits.socksproxy != conn->bits.socksproxy) + if(m->needle->bits.origin_is_proxy != conn->bits.origin_is_proxy) return FALSE; - if(m->needle->bits.socksproxy && - !proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) + if(!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) return FALSE; - if(m->needle->bits.httpproxy) { - if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy) - return FALSE; + if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy)) + return FALSE; - if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy)) + if(CURL_PROXY_IS_HTTPS(m->needle->http_proxy.proxytype)) { + /* https proxies come in different types, http/1.1, h2, ... */ + /* 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)); return FALSE; - - if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) { - /* https proxies come in different types, http/1.1, h2, ... */ - if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype) - return FALSE; - /* 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)); - return FALSE; - } - /* the SSL config to the server, which may apply here is checked - * further below */ } + /* the SSL config to the server, which may apply here is checked + * further below */ } return TRUE; } @@ -994,20 +951,15 @@ 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->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) || - Curl_timestrcmp(m->needle->passwd, conn->passwd) || - Curl_timestrcmp(m->needle->sasl_authzid, conn->sasl_authzid) || - Curl_timestrcmp(m->needle->oauth_bearer, conn->oauth_bearer)) { - /* one of them was different */ + if(!Curl_creds_same(m->needle->creds, conn->creds)) { + if(m->needle->creds) + return FALSE; + if(!Curl_creds_same(m->data->state.creds, conn->creds)) return FALSE; - } } -#ifdef HAVE_GSSAPI - /* GSS delegation differences do not actually affect every connection - and auth method, but this check takes precaution before efficiency */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + /* GSS delegation differences do not actually affect every connection and + auth method, but this check takes precaution before efficiency */ if(m->needle->gssapi_delegation != conn->gssapi_delegation) return FALSE; #endif @@ -1018,49 +970,29 @@ static bool url_match_auth(struct connectdata *conn, static bool url_match_destination(struct connectdata *conn, struct url_conn_match *m) { - /* Additional match requirements if talking TLS OR - * not talking to an HTTP proxy OR using a tunnel through a proxy */ - 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->scheme->name, conn->scheme->name)) { - /* `needle` and `conn` do not have the same scheme... */ - 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)); - return FALSE; - } - } - - /* If needle has "conn_to_*" set, conn must match this */ - 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)) - return FALSE; + /* Different connect-to peers never match */ + if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer)) + return FALSE; - /* hostname and port must match */ - if(!curl_strequal(m->needle->host.name, conn->host.name) || - m->needle->remote_port != conn->remote_port) + if(m->needle->origin->scheme != conn->origin->scheme) { + /* `needle` and `conn` not having the same scheme. + * This is allowed for the same family *if* conn is using TLS. + * - IMAP+STARTTLS works for IMAPS. + * - IMAPS works for IMAP. */ + if(get_protocol_family(conn->origin->scheme) != + m->needle->scheme->protocol) { return FALSE; + } } - return TRUE; + /* Scheme mismatch is acceptable, compare hostname/port */ + return Curl_peer_same_destination(m->needle->origin, conn->origin); } 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->scheme->flags & PROTOPT_SSL) && + /* If talking/upgrading to TLS, conn needs to use the same SSL options. */ + if(((m->needle->scheme->flags & PROTOPT_SSL) || m->may_tls) && !Curl_ssl_conn_config_match(m->data, conn, FALSE)) { DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T " has different SSL parameters, cannot reuse", @@ -1080,19 +1012,11 @@ static bool url_match_auth_ntlm(struct connectdata *conn, possible. (Especially we must not reuse the same connection if partway through a handshake!) */ 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 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; - } + if(conn->creds && + (!Curl_creds_same(conn->creds, m->data->state.creds) || + !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) { + /* connection credentials in play and not the same or not for the + * same origin. */ return FALSE; } } @@ -1106,13 +1030,10 @@ static bool url_match_auth_ntlm(struct connectdata *conn, if(m->want_proxy_ntlm_http) { /* Both conn->http_proxy.user and conn->http_proxy.passwd can be * NULL */ - if(!conn->http_proxy.user || !conn->http_proxy.passwd) + if(!conn->http_proxy.creds) 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)) + if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; } else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) { @@ -1152,8 +1073,9 @@ static bool url_match_auth_nego(struct connectdata *conn, 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)) + if(conn->creds && + (!Curl_creds_same(conn->creds, m->data->state.creds) || + !Curl_peer_equal(conn->creds_origin, m->data->state.origin))) return FALSE; } else if(conn->http_negotiate_state != GSS_AUTHNONE) { @@ -1166,13 +1088,10 @@ static bool url_match_auth_nego(struct connectdata *conn, 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) + if(!conn->http_proxy.creds) 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)) + if(!Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds)) return FALSE; } else if(conn->proxy_negotiate_state != GSS_AUTHNONE) { @@ -1222,6 +1141,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) if(!url_match_ssl_use(conn, m)) return FALSE; + if(!url_match_proxy_use(conn, m)) return FALSE; if(!url_match_ssl_config(conn, m)) @@ -1315,7 +1235,7 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_ntlm_http = - needle->bits.proxy_user_passwd && + needle->http_proxy.creds && (data->state.authproxy.want & CURLAUTH_NTLM) && (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif @@ -1327,12 +1247,13 @@ static bool url_attach_existing(struct Curl_easy *data, (needle->scheme->protocol & PROTO_FAMILY_HTTP); #ifndef CURL_DISABLE_PROXY match.want_proxy_nego_http = - needle->bits.proxy_user_passwd && + needle->http_proxy.creds && (data->state.authproxy.want & CURLAUTH_NEGOTIATE) && (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif #endif - match.req_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; + match.require_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; + match.may_tls = data->set.use_ssl > CURLUSESSL_NONE; /* Find a connection in the pool that matches what "data + needle" * requires. If a suitable candidate is found, it is attached to "data". */ @@ -1362,7 +1283,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->send_idx = 0; /* default for sending transfer data */ conn->connection_id = -1; /* no ID */ conn->attached_xfers = 0; - conn->remote_port = 0; /* unknown at this point */ /* Store creation time to help future close decision making */ conn->created = *Curl_pgrs_now(data); @@ -1370,29 +1290,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) /* Store current time to give a baseline to keepalive connection times. */ conn->keepalive = conn->created; -#ifndef CURL_DISABLE_PROXY - conn->http_proxy.proxytype = data->set.proxytype; - conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; - - /* 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]); - conn->bits.httpproxy = (conn->bits.proxy && - (conn->http_proxy.proxytype == CURLPROXY_HTTP || - conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 || - IS_HTTPS_PROXY(conn->http_proxy.proxytype))); - conn->bits.socksproxy = (conn->bits.proxy && !conn->bits.httpproxy); - - if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) { - conn->bits.proxy = TRUE; - conn->bits.socksproxy = TRUE; - } - - conn->bits.proxy_user_passwd = !!data->state.aptr.proxyuser; - conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; -#endif /* CURL_DISABLE_PROXY */ - #ifndef CURL_DISABLE_FTP conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; @@ -1417,10 +1314,9 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; conn->lastused = conn->created; -#ifdef HAVE_GSSAPI +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) conn->gssapi_delegation = data->set.gssapi_delegation; #endif - DEBUGF(infof(data, "alloc connection, bits.close=%d", conn->bits.close)); return conn; error: @@ -1429,36 +1325,25 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) return NULL; } -static CURLcode findprotocol(struct Curl_easy *data, - struct connectdata *conn, - const char *protostr) +static CURLcode url_set_conn_scheme(struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_scheme *scheme) { - const struct Curl_scheme *p = Curl_get_scheme(protostr); - - 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 - the result of a redirect */ - if(data->state.this_is_a_follow && - !(data->set.redir_protocols & p->protocol)) - /* nope, get out */ - ; - else { - /* Perform setup complement if some. */ - conn->scheme = conn->given = p; - /* 'port' and 'remote_port' are set in setup_connection_internals() */ - return CURLE_OK; - } + /* URL scheme is usable for connection when it is + * - allowed + * - not from a redirect or an allowed redirect protocol */ + if(scheme->run && + (data->set.allowed_protocols & scheme->protocol) && + (!data->state.this_is_a_follow || + (data->set.redir_protocols & scheme->protocol))) { + conn->scheme = conn->given = scheme; + return CURLE_OK; } - - /* The protocol was not found in the table, but we do not have to assign it - to anything since it is already assigned to a dummy-struct in the - 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)" : ""); - + if(scheme->flags & PROTOPT_NO_TRANSFER) + failf(data, "Protocol \"%s\" is not for transfers", scheme->name); + else + failf(data, "Protocol \"%s\" is disabled%s", scheme->name, + data->state.this_is_a_follow ? " (in redirect)" : ""); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -1476,269 +1361,273 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc) } } -#ifdef USE_IPV6 -/* - * If the URL was set with an IPv6 numerical address with a zone id part, set - * the scope_id based on that! - */ - -static void zonefrom_url(CURLU *uh, struct Curl_easy *data, - struct connectdata *conn) +#ifndef CURL_DISABLE_HSTS +static CURLcode hsts_upgrade(struct Curl_easy *data, + CURLU *uh, + uint16_t port_override, + uint32_t scope_id) { - char *zoneid; - CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); -#if !defined(HAVE_IF_NAMETOINDEX) || !defined(CURLVERBOSE) - (void)data; -#endif + /* HSTS upgrade */ + if(data->hsts && (data->state.origin->scheme == &Curl_scheme_http) && + Curl_hsts_applies(data->hsts, data->state.origin)) { + char *url; + CURLUcode uc; + CURLcode result; - if(!uc && zoneid) { - const char *p = zoneid; - curl_off_t scope; - if(!curlx_str_number(&p, &scope, UINT_MAX)) - /* A plain number, use it directly as a scope id. */ - conn->scope_id = (unsigned int)scope; -#ifdef HAVE_IF_NAMETOINDEX - else { - /* Zone identifier is not numeric */ - unsigned int scopeidx = 0; - scopeidx = if_nametoindex(zoneid); - if(!scopeidx) { -#ifdef CURLVERBOSE - char buffer[STRERROR_LEN]; - infof(data, "Invalid zoneid: %s; %s", zoneid, - curlx_strerror(errno, buffer, sizeof(buffer))); -#endif - } - else - conn->scope_id = scopeidx; - } -#endif /* HAVE_IF_NAMETOINDEX */ + curlx_safefree(data->state.up.scheme); + uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); + if(uc) + return Curl_uc_to_curlcode(uc); + 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); + Curl_bufref_set(&data->state.url, url, 0, curl_free); - curlx_free(zoneid); + result = Curl_peer_from_url(uh, data, port_override, scope_id, + &data->state.up, &data->state.origin); + if(result) + return result; + infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); } + return CURLE_OK; } #else -#define zonefrom_url(a, b, c) Curl_nop_stmt +#define hsts_upgrade(x, y, z, a) CURLE_OK #endif +#ifndef CURL_DISABLE_NETRC +static bool str_has_ctrl(const char *input) +{ + if(input) { + const unsigned char *str = (const unsigned char *)input; + while(*str) { + if(*str < 0x20) + return TRUE; + str++; + } + } + return FALSE; +} + /* - * Parse URL and fill in the relevant members of the connection struct. + * Override the login details from the URL with that in the CURLOPT_USERPWD + * option or a .netrc file, if applicable. */ -static CURLcode parseurlandfillconn(struct Curl_easy *data, - struct connectdata *conn) +static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, + struct Curl_creds **pcreds) { - CURLcode result; - 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 */ + struct Curl_creds *ncreds_out = NULL; + CURLcode result = CURLE_OK; - /* parse the URL */ - if(use_set_uh) { - uh = data->state.uh = curl_url_dup(data->set.uh); - } - else { - uh = data->state.uh = curl_url(); - } + if(data->set.use_netrc) { /* not CURL_NETRC_IGNORED */ + struct Curl_creds *ncreds_in = NULL; + bool scan_netrc = TRUE; + NETRCcode ret; + CURLUcode uc; - if(!uh) - return CURLE_OUT_OF_MEMORY; + if(*pcreds) { + switch((*pcreds)->source) { + case CREDS_OPTION: + /* we never override credentials set via CURLOPT_*, leave. */ + scan_netrc = FALSE; + break; + case CREDS_URL: /* only apply when netrc is not required */ + if(data->set.use_netrc == CURL_NETRC_REQUIRED) { + /* We ignore password from URL */ + ncreds_in = *pcreds; + } + else if(!Curl_creds_has_user(*pcreds) || + !Curl_creds_has_passwd(*pcreds)) { + /* We use netrc to complete what is missing */ + ncreds_in = *pcreds; + } + else + scan_netrc = FALSE; + break; + default: /* ignore credentials from other sources */ + break; + } + } - if(data->set.str[STRING_DEFAULT_PROTOCOL] && - !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; - Curl_bufref_set(&data->state.url, url, 0, curl_free); - } + if(!scan_netrc) + goto out; - if(!use_set_uh) { - char *newurl; - 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) | - (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); - if(uc) { - failf(data, "URL rejected: %s", curl_url_strerror(uc)); - return Curl_uc_to_curlcode(uc); + ret = Curl_netrc_scan(data, &data->state.netrc, + data->state.origin->hostname, + Curl_creds_user(ncreds_in), + data->set.str[STRING_NETRC_FILE], + &ncreds_out); + DEBUGASSERT(!ret || !ncreds_out); + if(ret == NETRC_OUT_OF_MEMORY) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - - /* after it was parsed, get the generated normalized version */ - uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - Curl_bufref_set(&data->state.url, newurl, 0, curl_free); + 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", + data->state.origin->hostname, + (data->set.str[STRING_NETRC_FILE] ? + data->set.str[STRING_NETRC_FILE] : ".netrc")); + } + else if(ret) { + const char *m = Curl_netrc_strerror(ret); + failf(data, ".netrc error: %s", m); + result = CURLE_READ_ERROR; + goto out; + } + else if(ncreds_out) { + if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL)) { + /* if the protocol cannot handle control codes in credentials, make + sure there are none */ + if(str_has_ctrl(ncreds_out->user) || + str_has_ctrl(ncreds_out->passwd)) { + failf(data, "control code detected in .netrc credentials"); + result = CURLE_READ_ERROR; + goto out; + } + } + CURL_TRC_M(data, "netrc: using credentials for %s as %s", + data->state.origin->hostname, ncreds_out->user); + result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, + *pcreds, CREDS_NETRC, pcreds); + if(result) + goto out; + /* for updated strings, we update them in the URL */ + uc = curl_url_set(data->state.uh, CURLUPART_USER, + Curl_creds_user(*pcreds), CURLU_URLENCODE); + if(!uc) + uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, + Curl_creds_passwd(*pcreds), + CURLU_URLENCODE); + if(uc) + result = Curl_uc_to_curlcode(uc); + } + else + DEBUGASSERT(0); } - uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - - uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); - if(uc) { - if(!curl_strequal("file", data->state.up.scheme)) - return CURLE_OUT_OF_MEMORY; - } - else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { - failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); - return CURLE_URL_MALFORMAT; - } +#ifdef CURLVERBOSE + Curl_creds_trace(data, data->state.creds, "transfer credentials"); +#endif - hostname = data->state.up.hostname; - hlen = hostname ? strlen(hostname) : 0; +out: + Curl_creds_unlink(&ncreds_out); + return result; +} +#endif /* CURL_DISABLE_NETRC */ - if(hostname && hostname[0] == '[') { - /* This looks like an IPv6 address literal. See if there is an address - scope. */ - /* cut off the brackets after copying this! */ - hostname++; - hlen -= 2; +static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) +{ + struct Curl_creds *newcreds = NULL; + CURLcode result = CURLE_OK; - zonefrom_url(uh, data, conn); + if((data->set.str[STRING_USERNAME] || + data->set.str[STRING_PASSWORD] || + data->set.str[STRING_BEARER] || + data->set.str[STRING_SASL_AUTHZID] || + data->set.str[STRING_SERVICE_NAME]) && + Curl_auth_allowed_to_origin(data, data->state.origin)) { + result = Curl_creds_create(data->set.str[STRING_USERNAME], + data->set.str[STRING_PASSWORD], + data->set.str[STRING_BEARER], + data->set.str[STRING_SASL_AUTHZID], + data->set.str[STRING_SERVICE_NAME], + CREDS_OPTION, &newcreds); + if(result) + goto out; } - /* make sure the connect struct gets its own copy of the 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; - - /************************************************************* - * IDN-convert the hostnames - *************************************************************/ - result = Curl_idnconvert_hostname(&conn->host); - if(result) - return result; + /* Extract credentials from the URL only if there are none OR + * if no CURLOPT_USER was set. */ + if(!newcreds || !Curl_creds_has_user(newcreds)) { + char *udecoded = NULL; + char *pdecoded = NULL; + CURLUcode uc; -#ifndef CURL_DISABLE_HSTS - /* HSTS upgrade */ - if(data->hsts && curl_strequal("http", data->state.up.scheme)) { - /* This MUST use the IDN decoded name */ - if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { - char *url; - curlx_safefree(data->state.up.scheme); - uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); - if(uc) - return Curl_uc_to_curlcode(uc); - 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) { - curlx_free(url); - return Curl_uc_to_curlcode(uc); - } - Curl_bufref_set(&data->state.url, url, 0, curl_free); - infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); + uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); + if(uc && (uc != CURLUE_NO_USER)) + result = Curl_uc_to_curlcode(uc); + if(!result) { + uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); + if(uc && (uc != CURLUE_NO_PASSWORD)) + result = Curl_uc_to_curlcode(uc); + } + if(!result && data->state.up.user) { + result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL, + (data->state.origin->scheme->flags & + PROTOPT_USERPWDCTRL) ? + REJECT_ZERO : REJECT_CTRL); } - } -#endif - - result = findprotocol(data, conn, data->state.up.scheme); - if(result) - return result; - - /* - * username and password set with their own options override the credentials - * possibly set in the URL, but netrc does not. - */ - if(!data->state.aptr.passwd || (data->state.creds_from != CREDS_OPTION)) { - uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL, - conn->scheme->flags&PROTOPT_USERPWDCTRL ? + if(!result && data->state.up.password) { + result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL, + (data->state.origin->scheme->flags & + PROTOPT_USERPWDCTRL) ? REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->passwd = decoded; - result = Curl_setstropt(&data->state.aptr.passwd, decoded); - if(result) - return result; - data->state.creds_from = CREDS_URL; } - else if(uc != CURLUE_NO_PASSWORD) - return Curl_uc_to_curlcode(uc); - } + if(!result) + result = Curl_creds_merge(udecoded, pdecoded, newcreds, + CREDS_URL, &newcreds); - if(!data->state.aptr.user || (data->state.creds_from != CREDS_OPTION)) { - /* we do not use the URL API's URL decoder option here since it rejects - control codes and we want to allow them for some schemes in the user - and password fields */ - uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0); - if(!uc) { - char *decoded; - result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL, - conn->scheme->flags&PROTOPT_USERPWDCTRL ? - REJECT_ZERO : REJECT_CTRL); - if(result) - return result; - conn->user = decoded; - result = Curl_setstropt(&data->state.aptr.user, decoded); - data->state.creds_from = CREDS_URL; + curlx_free(udecoded); + curlx_free(pdecoded); + if(result) { + failf(data, "error extracting credentials from URL"); + goto out; } - else if(uc != CURLUE_NO_USER) - return Curl_uc_to_curlcode(uc); - if(result) - return result; } - uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, - CURLU_URLDECODE); - if(!uc) { - conn->options = curlx_strdup(data->state.up.options); - if(!conn->options) - return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_NETRC + /* Check for overridden login details and set them accordingly so that + they are known when protocol->setup_connection is called! */ + result = url_set_data_creds_netrc(data, &newcreds); +#endif /* CURL_DISABLE_NETRC */ + +out: + if(!result && !Curl_creds_equal(data->state.creds, newcreds)) { + /* Do we have more things to trigger on credentials change? */ + Curl_creds_link(&data->state.creds, newcreds); } - else if(uc != CURLUE_NO_OPTIONS) - return Curl_uc_to_curlcode(uc); + Curl_creds_unlink(&newcreds); + return result; +} - uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); +static CURLcode url_set_conn_origin_etc(struct Curl_easy *data, + struct connectdata *conn) +{ + CURLcode result = CURLE_OK; - uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, - CURLU_DEFAULT_PORT); - if(uc) { - if((uc == CURLUE_OUT_OF_MEMORY) || - !curl_strequal("file", data->state.up.scheme)) - return CURLE_OUT_OF_MEMORY; + Curl_peer_link(&conn->origin, data->state.origin); + + /* set the connection scheme */ + result = url_set_conn_scheme(data, conn, conn->origin->scheme); + if(result) + goto out; + + /* set the connection options */ + if(data->set.str[STRING_OPTIONS]) { + conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]); + if(!conn->options) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } } - else { - curl_off_t port; - bool valid = TRUE; - if(data->set.use_port && data->state.allow_port) - port = data->set.use_port; - else { - const char *p = data->state.up.port; - if(curlx_str_number(&p, &port, 0xffff)) - valid = FALSE; + else if(data->state.up.options) { + conn->options = curlx_strdup(data->state.up.options); + if(!conn->options) { + result = CURLE_OUT_OF_MEMORY; + goto out; } - if(valid) - conn->remote_port = (unsigned short)port; } - 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. */ - conn->scope_id = data->set.scope_id; + conn->scope_id = data->set.scope_id ? + data->set.scope_id : data->state.origin->scopeid; #endif - return CURLE_OK; +out: + return result; } /* @@ -1784,42 +1673,31 @@ static CURLcode setup_range(struct Curl_easy *data) static CURLcode setup_connection_internals(struct Curl_easy *data, struct connectdata *conn) { - const char *hostname; - uint16_t port; + struct Curl_peer *peer = NULL; CURLcode result; - 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 - if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { - hostname = conn->http_proxy.host.name; - port = conn->http_proxy.port; - } - else -#endif - { - 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; - } + peer = Curl_conn_get_destination(conn, FIRSTSOCKET); + if(!peer) + return CURLE_FAILED_INIT; -#ifdef USE_IPV6 /* 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]%%%u", hostname, port, - conn->scope_id); + if(peer->ipv6) + if(peer->scopeid) + conn->destination = curl_maprintf("[%s%%%u]:%u", + peer->hostname, peer->scopeid, peer->port); + else + conn->destination = curl_maprintf("[%s]:%u", + peer->hostname, peer->port); else -#endif - conn->destination = curl_maprintf("%s:%u", hostname, port); + conn->destination = curl_maprintf("%s:%u", peer->hostname, peer->port); if(!conn->destination) return CURLE_OUT_OF_MEMORY; @@ -1829,500 +1707,6 @@ 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) - ****************************************************************/ -static char *detect_proxy(struct Curl_easy *data, - struct connectdata *conn) -{ - char *proxy = NULL; - - /* If proxy was not specified, we check for default proxy environment - * variables, to enable i.e Lynx compliance: - * - * http_proxy=http://some.server.dom:port/ - * https_proxy=http://some.server.dom:port/ - * ftp_proxy=http://some.server.dom:port/ - * no_proxy=domain1.dom,host.domain2.dom - * (a comma-separated list of hosts which should - * not be proxied, or an asterisk to override - * all proxy variables) - * all_proxy=http://some.server.dom:port/ - * (seems to exist for the CERN www lib. Probably - * the first to check for.) - * - * For compatibility, the all-uppercase versions of these variables are - * checked if the lowercase versions do not exist. - */ - char proxy_env[20]; - const char *envp; - VERBOSE(envp = proxy_env); - - curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", - conn->scheme->name); - - /* read the protocol proxy: */ - proxy = curl_getenv(proxy_env); - - /* - * We do not try the uppercase version of HTTP_PROXY because of - * security reasons: - * - * When curl is used in a webserver application - * environment (cgi or php), this environment variable can - * be controlled by the web server user by setting the - * http header 'Proxy:' to some value. - * - * This can cause 'internal' http/ftp requests to be - * arbitrarily redirected by any external attacker. - */ - if(!proxy && !curl_strequal("http_proxy", proxy_env)) { - /* There was no lowercase variable, try the uppercase version: */ - Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); - proxy = curl_getenv(proxy_env); - } - - if(!proxy) { -#ifndef CURL_DISABLE_WEBSOCKETS - /* websocket proxy fallbacks */ - if(curl_strequal("ws_proxy", proxy_env)) { - proxy = curl_getenv("http_proxy"); - } - else if(curl_strequal("wss_proxy", proxy_env)) { - proxy = curl_getenv("https_proxy"); - if(!proxy) - proxy = curl_getenv("HTTPS_PROXY"); - } - if(!proxy) { -#endif - envp = "all_proxy"; - proxy = curl_getenv(envp); /* default proxy to use */ - if(!proxy) { - envp = "ALL_PROXY"; - proxy = curl_getenv(envp); - } -#ifndef CURL_DISABLE_WEBSOCKETS - } -#endif - } - if(proxy) - infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); - - return proxy; -} -#endif /* CURL_DISABLE_HTTP */ - -/* - * If this is supposed to use a proxy, we need to figure out the proxy - * hostname, so that we can reuse an existing connection - * that may exist registered to the same proxy host. - */ -static CURLcode parse_proxy(struct Curl_easy *data, - struct connectdata *conn, const char *proxy, - long proxytype) -{ - char *portptr = NULL; - char *proxyuser = NULL; - char *proxypasswd = NULL; - char *host = NULL; - bool sockstype; - CURLUcode uc; - struct proxy_info *proxyinfo; - CURLU *uhp = curl_url(); - CURLcode result = CURLE_OK; - char *scheme = NULL; -#ifdef USE_UNIX_SOCKETS - char *path = NULL; - bool is_unix_proxy = FALSE; -#endif - - if(!uhp) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - /* 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); - if(!uc) { - /* parsed okay as a URL */ - uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - - if(curl_strequal("https", scheme)) { - if(proxytype != CURLPROXY_HTTPS2) - proxytype = CURLPROXY_HTTPS; - else - proxytype = CURLPROXY_HTTPS2; - } - else if(curl_strequal("socks5h", scheme)) - proxytype = CURLPROXY_SOCKS5_HOSTNAME; - else if(curl_strequal("socks5", scheme)) - proxytype = CURLPROXY_SOCKS5; - else if(curl_strequal("socks4a", scheme)) - proxytype = CURLPROXY_SOCKS4A; - else if(curl_strequal("socks4", scheme) || - curl_strequal("socks", scheme)) - proxytype = CURLPROXY_SOCKS4; - else if(curl_strequal("http", scheme)) - ; /* leave it as HTTP or HTTP/1.0 */ - else { - /* Any other xxx:// reject! */ - failf(data, "Unsupported proxy scheme for \'%s\'", proxy); - result = CURLE_COULDNT_CONNECT; - goto error; - } - } - else { - failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, - curl_url_strerror(uc)); - result = CURLE_COULDNT_RESOLVE_PROXY; - goto error; - } - - if(IS_HTTPS_PROXY(proxytype) && - !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { - failf(data, "Unsupported proxy \'%s\', libcurl is built without the " - "HTTPS-proxy support.", proxy); - result = CURLE_NOT_BUILT_IN; - goto error; - } - - sockstype = - proxytype == CURLPROXY_SOCKS5_HOSTNAME || - proxytype == CURLPROXY_SOCKS5 || - proxytype == CURLPROXY_SOCKS4A || - proxytype == CURLPROXY_SOCKS4; - - proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; - proxyinfo->proxytype = (unsigned char)proxytype; - - /* 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)) { - result = Curl_uc_to_curlcode(uc); - goto error; - } - uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); - if(uc && (uc != CURLUE_NO_PASSWORD)) { - result = Curl_uc_to_curlcode(uc); - goto error; - } - - if(proxyuser || proxypasswd) { - curlx_free(proxyinfo->user); - proxyinfo->user = proxyuser; - result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser); - proxyuser = NULL; - if(result) - goto error; - curlx_safefree(proxyinfo->passwd); - if(!proxypasswd) { - proxypasswd = curlx_strdup(""); - if(!proxypasswd) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - } - proxyinfo->passwd = proxypasswd; - result = Curl_setstropt(&data->state.aptr.proxypasswd, proxypasswd); - proxypasswd = NULL; - if(result) - goto error; - conn->bits.proxy_user_passwd = TRUE; /* enable it */ - } - - 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, 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 */ - proxyinfo->port = data->set.proxyport; - else { - if(IS_HTTPS_PROXY(proxytype)) - proxyinfo->port = CURL_DEFAULT_HTTPS_PROXY_PORT; - else - proxyinfo->port = CURL_DEFAULT_PROXY_PORT; - } - } - - /* now, clone the proxy hostname */ - uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } -#ifdef USE_UNIX_SOCKETS - if(sockstype && curl_strequal(UNIX_SOCKET_PREFIX, host)) { - uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE); - if(uc) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - /* path will be "/", if no path was found */ - if(strcmp("/", path)) { - is_unix_proxy = TRUE; - curlx_free(host); - host = curl_maprintf(UNIX_SOCKET_PREFIX "%s", path); - if(!host) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - curlx_free(proxyinfo->host.rawalloc); - proxyinfo->host.rawalloc = host; - proxyinfo->host.name = host; - host = NULL; - } - } - - if(!is_unix_proxy) { -#endif - 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++; - zonefrom_url(uhp, data, conn); - } - proxyinfo->host.name = host; - host = NULL; -#ifdef USE_UNIX_SOCKETS - } -#endif - -error: - curlx_free(proxyuser); - curlx_free(proxypasswd); - curlx_free(host); - curlx_free(scheme); -#ifdef USE_UNIX_SOCKETS - curlx_free(path); -#endif - curl_url_cleanup(uhp); - return result; -} - -/* - * Extract the user and password from the authentication string - */ -static CURLcode parse_proxy_auth(struct Curl_easy *data, - struct connectdata *conn) -{ - const char *proxyuser = data->state.aptr.proxyuser ? - data->state.aptr.proxyuser : ""; - const char *proxypasswd = data->state.aptr.proxypasswd ? - data->state.aptr.proxypasswd : ""; - CURLcode result = CURLE_OUT_OF_MEMORY; - - conn->http_proxy.user = curlx_strdup(proxyuser); - if(conn->http_proxy.user) { - conn->http_proxy.passwd = curlx_strdup(proxypasswd); - if(conn->http_proxy.passwd) - result = CURLE_OK; - else - curlx_safefree(conn->http_proxy.user); - } - return result; -} - -/* create_conn helper to parse and init proxy values. to be called after Unix - socket init but before any proxy vars are evaluated. */ -static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data, - struct connectdata *conn) -{ - char *proxy = NULL; - char *socksproxy = NULL; - char *no_proxy = NULL; - CURLcode result = CURLE_OK; - - /************************************************************* - * Extract the user and password from the authentication string - *************************************************************/ - if(conn->bits.proxy_user_passwd) { - result = parse_proxy_auth(data, conn); - if(result) - goto out; - } - - /************************************************************* - * Detect what (if any) proxy to use - *************************************************************/ - if(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"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(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"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - - if(!data->set.str[STRING_NOPROXY]) { - const char *p = "no_proxy"; - no_proxy = curl_getenv(p); - if(!no_proxy) { - p = "NO_PROXY"; - no_proxy = curl_getenv(p); - } - if(no_proxy) { - infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy); - } - } - - if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? - data->set.str[STRING_NOPROXY] : no_proxy)) { - 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 */ - 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) { - curlx_free(proxy); - proxy = NULL; - } -#endif - - 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->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; - } - - /*********************************************************************** - * If this is supposed to use a proxy, we need to figure out the proxy host - * name, proxy type and port number, so that we can reuse an existing - * connection that may exist registered to the same proxy host. - ***********************************************************************/ - if(proxy || socksproxy) { - if(proxy) { - 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, - conn->socks_proxy.proxytype); - /* parse_proxy copies the socks proxy string */ - curlx_safefree(socksproxy); - if(result) - goto out; - } - - if(conn->http_proxy.host.rawalloc) { -#ifdef CURL_DISABLE_HTTP - /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ - result = CURLE_UNSUPPORTED_PROTOCOL; - goto out; -#else - /* force this connection's protocol to become HTTP if compatible */ - if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { - if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) && - !conn->bits.tunnel_proxy) - conn->scheme = &Curl_scheme_http; - else - /* if not converting to HTTP over the proxy, enforce tunneling */ - conn->bits.tunnel_proxy = TRUE; - } - conn->bits.httpproxy = TRUE; -#endif - } - else { - conn->bits.httpproxy = FALSE; /* not an HTTP proxy */ - conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ - } - - if(conn->socks_proxy.host.rawalloc) { - if(!conn->http_proxy.host.rawalloc) { - /* once a socks proxy */ - if(!conn->socks_proxy.user) { - conn->socks_proxy.user = conn->http_proxy.user; - conn->http_proxy.user = NULL; - curlx_free(conn->socks_proxy.passwd); - conn->socks_proxy.passwd = conn->http_proxy.passwd; - conn->http_proxy.passwd = NULL; - } - } - conn->bits.socksproxy = TRUE; - } - else - conn->bits.socksproxy = FALSE; /* not a socks proxy */ - } - else { - conn->bits.socksproxy = FALSE; - conn->bits.httpproxy = FALSE; - } - conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; - - if(!conn->bits.proxy) { - /* we are not using the proxy after all... */ - conn->bits.proxy = FALSE; - conn->bits.httpproxy = FALSE; - conn->bits.socksproxy = FALSE; - conn->bits.proxy_user_passwd = FALSE; - conn->bits.tunnel_proxy = FALSE; - /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need - to signal that CURLPROXY_HTTPS is not used for this connection */ - conn->http_proxy.proxytype = CURLPROXY_HTTP; - } - -out: - - curlx_free(socksproxy); - curlx_free(proxy); - return result; -} -#endif /* CURL_DISABLE_PROXY */ - /* * Curl_parse_login_details() * @@ -2411,309 +1795,35 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len, *userp = ubuf; *passwdp = pbuf; return CURLE_OK; -error: - curlx_free(ubuf); - curlx_free(pbuf); - return CURLE_OUT_OF_MEMORY; -} - -/************************************************************* - * Figure out the remote port number and fix it in the URL - * - * No matter if we use a proxy or not, we have to figure out the remote - * port number of various reasons. - * - * The port number embedded in the URL is replaced, if necessary. - *************************************************************/ -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; - 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; - } - - return CURLE_OK; -} - -#ifndef CURL_DISABLE_NETRC -static bool str_has_ctrl(const char *input) -{ - if(input) { - const unsigned char *str = (const unsigned char *)input; - while(*str) { - if(*str < 0x20) - return TRUE; - str++; - } - } - return FALSE; -} -#endif - -/* - * Override the login details from the URL with that in the CURLOPT_USERPWD - * option or a .netrc file, if applicable. - */ -static CURLcode override_login(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLUcode uc; - char **userp = &conn->user; - char **passwdp = &conn->passwd; - char **optionsp = &conn->options; - - if(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) { - curlx_safefree(*userp); - curlx_safefree(*passwdp); - } - conn->bits.netrc = FALSE; - if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { - bool url_provided = FALSE; - - if(data->state.aptr.user && - (data->state.creds_from != CREDS_NETRC)) { - /* there was a username with a length in the URL. Use the URL decoded - version */ - userp = &data->state.aptr.user; - url_provided = TRUE; - } - - if(!*passwdp) { - NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, - userp, passwdp, - data->set.str[STRING_NETRC_FILE]); - 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")); - } - else if(ret) { - const char *m = Curl_netrc_strerror(ret); - failf(data, ".netrc error: %s", m); - return CURLE_READ_ERROR; - } - else { - 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"); - return CURLE_READ_ERROR; - } - } - /* set bits.netrc TRUE to remember that we got the name from a .netrc - file, so that it is safe to use even if we followed a Location: to a - different host or similar. */ - conn->bits.netrc = TRUE; - } - } - if(url_provided) { - 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 = curlx_strdup(""); - if(!*userp) - return CURLE_OUT_OF_MEMORY; - } - } -#endif - - /* for updated strings, we update them in the URL */ - if(*userp) { - CURLcode result; - if(data->state.aptr.user != *userp) { - /* nothing to do then */ - result = Curl_setstropt(&data->state.aptr.user, *userp); - if(result) - return result; - data->state.creds_from = CREDS_NETRC; - } - } - if(data->state.aptr.user) { - uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user, - CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*userp) { - *userp = curlx_strdup(data->state.aptr.user); - if(!*userp) - return CURLE_OUT_OF_MEMORY; - } - } - if(*passwdp) { - CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp); - if(result) - return result; - data->state.creds_from = CREDS_NETRC; - } - if(data->state.aptr.passwd) { - uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD, - data->state.aptr.passwd, CURLU_URLENCODE); - if(uc) - return Curl_uc_to_curlcode(uc); - if(!*passwdp) { - *passwdp = curlx_strdup(data->state.aptr.passwd); - if(!*passwdp) - return CURLE_OUT_OF_MEMORY; - } - } - - return CURLE_OK; -} - -/* - * Set the login details so they are available in the connection - */ -static CURLcode set_login(struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result = CURLE_OK; - const char *setuser = CURL_DEFAULT_USER; - const char *setpasswd = CURL_DEFAULT_PASSWORD; - - /* If our protocol needs a password and we have none, use the defaults */ - if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user) - ; - else { - setuser = ""; - setpasswd = ""; - } - /* Store the default user */ - if(!conn->user) { - conn->user = curlx_strdup(setuser); - if(!conn->user) - return CURLE_OUT_OF_MEMORY; - } - - /* Store the default password */ - if(!conn->passwd) { - conn->passwd = curlx_strdup(setpasswd); - if(!conn->passwd) - result = CURLE_OUT_OF_MEMORY; - } - - return result; +error: + curlx_free(ubuf); + curlx_free(pbuf); + return CURLE_OUT_OF_MEMORY; } /* - * Parses a "host:port" string to connect to. - * The hostname and the port may be empty; in this case, NULL is returned for - * the hostname and -1 for the port. + * Set the login details so they are available in the connection */ -static CURLcode parse_connect_to_host_port(struct Curl_easy *data, - const char *host, - char **hostname_result, - int *port_result) +static CURLcode url_set_conn_login(struct Curl_easy *data, + struct connectdata *conn) { - char *host_dup; - char *hostptr; - char *host_portno; - char *portptr; - int port = -1; - CURLcode result = CURLE_OK; - - *hostname_result = NULL; - *port_result = -1; - - if(!host || !*host) - return CURLE_OK; - - host_dup = curlx_strdup(host); - if(!host_dup) - return CURLE_OUT_OF_MEMORY; - - hostptr = host_dup; - - /* start scanning for port number at this point */ - portptr = hostptr; - - /* detect and extract RFC6874-style IPv6-addresses */ - if(*hostptr == '[') { -#ifdef USE_IPV6 - char *ptr = ++hostptr; /* advance beyond the initial bracket */ - while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) - ptr++; - if(*ptr == '%') { - /* There might be a zone identifier */ - if(strncmp("%25", ptr, 3)) - infof(data, "Please URL encode %% as %%25, see RFC 6874."); - ptr++; - /* Allow unreserved characters as defined in RFC 3986 */ - while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') || - (*ptr == '.') || (*ptr == '_') || (*ptr == '~'))) - ptr++; - } - if(*ptr == ']') - /* yeps, it ended nicely with a bracket as well */ - *ptr++ = '\0'; + /* If our protocol needs a password and we have none, use the defaults */ + if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !conn->creds) { + Curl_peer_link(&conn->creds_origin, data->state.origin); + if(data->state.creds) + Curl_creds_link(&conn->creds, data->state.creds); else - infof(data, "Invalid IPv6 address format"); - portptr = ptr; - /* Note that if this did not end with a bracket, we still advanced the - * hostptr first, but I cannot see anything wrong with that as no host - * name nor a numeric can legally start with a bracket. - */ -#else - failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in"); - result = CURLE_NOT_BUILT_IN; - goto error; -#endif - } - - /* Get port number off server.com:1080 */ - host_portno = strchr(portptr, ':'); - if(host_portno) { - *host_portno = '\0'; /* cut off number from hostname */ - host_portno++; - if(*host_portno) { - curl_off_t portparse; - const char *p = host_portno; - if(curlx_str_number(&p, &portparse, 0xffff)) { - failf(data, "No valid port number in connect to host string (%s)", - host_portno); - result = CURLE_SETOPT_OPTION_SYNTAX; - goto error; - } - port = (int)portparse; /* we know it will fit */ - } + return Curl_creds_create(CURL_DEFAULT_USER, CURL_DEFAULT_PASSWORD, + NULL, NULL, NULL, CREDS_NONE, &conn->creds); } - - /* now, clone the cleaned hostname */ - DEBUGASSERT(hostptr); - *hostname_result = curlx_strdup(hostptr); - if(!*hostname_result) { - result = CURLE_OUT_OF_MEMORY; - goto error; + else if(!(conn->scheme->flags & PROTOPT_CREDSPERREQUEST)) { + /* for protocols that do not handle credentials per request, + * the connection credentials are set by the initial transfer. */ + Curl_peer_link(&conn->creds_origin, data->state.origin); + Curl_creds_link(&conn->creds, data->state.creds); } - *port_result = port; - -error: - curlx_free(host_dup); - return result; + return CURLE_OK; } /* @@ -2721,18 +1831,16 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT". */ static CURLcode parse_connect_to_string(struct Curl_easy *data, - struct connectdata *conn, - const char *conn_to_host, - char **host_result, - int *port_result) + const struct Curl_peer *dest, + const char *conn_to_line, + struct Curl_peer **pvia_dest) { CURLcode result = CURLE_OK; - const char *ptr = conn_to_host; + const char *ptr = conn_to_line; bool host_match = FALSE; bool port_match = FALSE; - *host_result = NULL; - *port_result = -1; + *pvia_dest = NULL; if(*ptr == ':') { /* an empty hostname always matches */ @@ -2743,9 +1851,8 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, /* 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; + const char *hostname_to_match = (dest->user_hostname[0] == '[') ? + dest->user_hostname : dest->hostname; size_t hlen = strlen(hostname_to_match); host_match = curl_strnequal(ptr, hostname_to_match, hlen); ptr += hlen; @@ -2766,68 +1873,44 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, if(ptr_next) { curl_off_t port_to_match; if(!curlx_str_number(&ptr, &port_to_match, 0xffff) && - (port_to_match == (curl_off_t)conn->remote_port)) + ((uint16_t)port_to_match == dest->port)) { port_match = TRUE; + } ptr = ptr_next + 1; } } } - if(host_match && port_match) { - /* parse the hostname and port to connect to */ - result = parse_connect_to_host_port(data, ptr, host_result, port_result); - } + if(host_match && port_match && ptr && *ptr) + result = Curl_peer_from_connect_to(data, dest, ptr, pvia_dest); return result; } -/* - * Processes all strings in the "connect to" slist, and uses the "connect - * to host" and "connect to port" of the first string that matches. - */ -static CURLcode parse_connect_to_slist(struct Curl_easy *data, - struct connectdata *conn, - struct curl_slist *conn_to_host) +/* With `conn->origin` known, determine if we should talk to that + * directly or via another peer. This is the result of inspecting + * the "connect to" slist and "alt-svc" settings. */ +static CURLcode url_set_conn_peer(struct Curl_easy *data, + struct connectdata *conn) { CURLcode result = CURLE_OK; - char *host = NULL; - int port = -1; + const struct Curl_peer *origin = conn->origin; + struct Curl_peer *via_peer = NULL; + struct curl_slist *conn_to_entry = data->set.connect_to; + + DEBUGASSERT(!conn->via_peer); + Curl_peer_unlink(&conn->via_peer); - while(conn_to_host && !host && port == -1) { - result = parse_connect_to_string(data, conn, conn_to_host->data, - &host, &port); + while(conn_to_entry && !via_peer) { + result = parse_connect_to_string(data, origin, conn_to_entry->data, + &via_peer); if(result) return result; - - if(host && *host) { - conn->conn_to_host.rawalloc = host; - conn->conn_to_host.name = host; - conn->bits.conn_to_host = TRUE; - - infof(data, "Connecting to hostname: %s", host); - } - else { - /* no "connect to host" */ - conn->bits.conn_to_host = FALSE; - curlx_safefree(host); - } - - if(port >= 0) { - conn->conn_to_port = (uint16_t)port; - conn->bits.conn_to_port = TRUE; - infof(data, "Connecting to port: %u", conn->conn_to_port); - } - else { - /* no "connect to port" */ - conn->bits.conn_to_port = FALSE; - port = -1; - } - - conn_to_host = conn_to_host->next; + conn_to_entry = conn_to_entry->next; } #ifndef CURL_DISABLE_ALTSVC - if(data->asi && !host && (port == -1) && + if(data->asi && !via_peer && ((conn->scheme->protocol == CURLPROTO_HTTPS) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ @@ -2858,13 +1941,13 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, allowed_alpns |= ALPN_h1; allowed_alpns &= (int)data->asi->flags; - host = conn->host.rawalloc; - DEBUGF(infof(data, "check Alt-Svc for host %s", host)); + DEBUGF(infof(data, "check Alt-Svc for host '%s'", origin->hostname)); #ifdef USE_HTTP3 if(!hit && (neg->wanted & CURL_HTTP_V3x)) { srcalpnid = ALPN_h3; hit = Curl_altsvc_lookup(data->asi, - ALPN_h3, host, conn->remote_port, /* from */ + ALPN_h3, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2874,7 +1957,8 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, !neg->h2_prior_knowledge) { srcalpnid = ALPN_h2; hit = Curl_altsvc_lookup(data->asi, - ALPN_h2, host, conn->remote_port, /* from */ + ALPN_h2, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2883,7 +1967,8 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, !neg->only_10) { srcalpnid = ALPN_h1; hit = Curl_altsvc_lookup(data->asi, - ALPN_h1, host, conn->remote_port, /* from */ + ALPN_h1, origin->hostname, + origin->port, /* from */ &as /* to */, allowed_alpns, &same_dest); } @@ -2908,18 +1993,16 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, } } else if(hit) { - char *hostd = curlx_strdup(as->dst.host); - if(!hostd) - return CURLE_OUT_OF_MEMORY; - conn->conn_to_host.rawalloc = hostd; - conn->conn_to_host.name = hostd; - conn->bits.conn_to_host = TRUE; - conn->conn_to_port = as->dst.port; - conn->bits.conn_to_port = TRUE; - conn->bits.altused = TRUE; + result = Curl_peer_create(data, conn->origin->scheme, + as->dst.host, as->dst.port, + &via_peer); + if(result) + return result; 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); + Curl_alpnid2str(srcalpnid), origin->hostname, origin->port, + Curl_alpnid2str(as->dst.alpnid), + via_peer->hostname, via_peer->port); + conn->bits.altused = TRUE; if(srcalpnid != as->dst.alpnid) { /* protocol version switch */ switch(as->dst.alpnid) { @@ -2942,15 +2025,10 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, } #endif - return result; -} + if(via_peer) + conn->via_peer = via_peer; -static void url_move_hostname(struct hostname *dest, struct hostname *src) -{ - curlx_safefree(dest->rawalloc); - Curl_free_idnconverted_hostname(dest); - *dest = *src; - memset(src, 0, sizeof(*src)); + return result; } /* @@ -2963,51 +2041,32 @@ static void url_conn_reuse_adjust(struct Curl_easy *data, /* get the user+password information from the needle since it may * be new for this request even when we reuse conn */ - if(needle->user) { + if(needle->creds) { /* use the new username and password though */ - curlx_free(conn->user); - curlx_free(conn->passwd); - conn->user = needle->user; - conn->passwd = needle->passwd; - needle->user = NULL; - needle->passwd = NULL; + Curl_creds_link(&conn->creds, needle->creds); } #ifndef CURL_DISABLE_PROXY - conn->bits.proxy_user_passwd = needle->bits.proxy_user_passwd; - if(conn->bits.proxy_user_passwd) { - /* use the new proxy username and proxy password though */ - curlx_free(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; - } + /* use the new proxy username and proxy password though */ + Curl_creds_link(&conn->http_proxy.creds, needle->http_proxy.creds); + Curl_creds_link(&conn->socks_proxy.creds, needle->socks_proxy.creds); #endif /* Finding a connection for reuse in the cpool matches, among other * things on the "remote-relevant" hostname. This is not necessarily - * the authority of the URL, e.g. conn->host. For example: + * the authority of the URL, e.g. conn->origin. For example: * - we use a proxy (not tunneling). we want to send all requests * that use the same proxy on this connection. * - we have a "connect-to" setting that may redirect the hostname of * 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 + * Since connection reuse does not match on conn->origin necessarily, we * switch conn to needle's host settings. */ - url_move_hostname(&conn->host, &needle->host); - url_move_hostname(&conn->conn_to_host, &needle->conn_to_host); - - conn->conn_to_port = needle->conn_to_port; - conn->remote_port = needle->remote_port; + Curl_peer_link(&conn->origin, needle->origin); + Curl_peer_link(&conn->via_peer, needle->via_peer); + Curl_peer_link(&conn->origin2, needle->origin2); + Curl_peer_link(&conn->via_peer2, needle->via_peer2); } static void conn_meta_freeentry(void *p) @@ -3016,7 +2075,7 @@ static void conn_meta_freeentry(void *p) /* 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); + DEBUGASSERT(!p); } static CURLcode url_create_needle(struct Curl_easy *data, @@ -3024,14 +2083,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, { struct connectdata *needle = NULL; CURLcode result = CURLE_OK; - - /************************************************************* - * Check input data - *************************************************************/ - if(!Curl_bufref_ptr(&data->state.url)) { - result = CURLE_URL_MALFORMAT; - goto out; - } + bool network_scheme = TRUE; /* almost all are */ /* First, split up the current URL in parts so that we can use the parts for checking against the already present connections. In order @@ -3047,126 +2099,64 @@ static CURLcode url_create_needle(struct Curl_easy *data, Curl_hash_init(&needle->meta_hash, 23, Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry); - result = parseurlandfillconn(data, needle); - if(result) - goto out; - - if(data->set.str[STRING_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]) { - needle->oauth_bearer = curlx_strdup(data->set.str[STRING_BEARER]); - if(!needle->oauth_bearer) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - -#ifdef USE_UNIX_SOCKETS - if(data->set.str[STRING_UNIX_SOCKET_PATH]) { - 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; - } - 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, needle); - if(result) - goto out; - - /************************************************************* - * If the protocol is using SSL and HTTP proxy is used, we set - * the tunnel_proxy bit. - *************************************************************/ - 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 + * Determine `conn->origin` and populate `data->state.up` and + * other URL related properties. *************************************************************/ - result = parse_remote_port(data, needle); + result = url_set_conn_origin_etc(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, needle); - if(result) - goto out; - - result = set_login(data, needle); /* default credentials */ - if(result) - goto out; - - /************************************************************* - * 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, needle, data->set.connect_to); - if(result) - goto out; + DEBUGASSERT(needle->origin); + network_scheme = !(needle->origin->scheme->flags & PROTOPT_NONETWORK); +#ifdef USE_UNIX_SOCKETS /************************************************************* - * IDN-convert the proxy hostnames + * Set UDS first. It overrides "via_peer" and proxy settings. *************************************************************/ -#ifndef CURL_DISABLE_PROXY - if(needle->bits.httpproxy) { - result = Curl_idnconvert_hostname(&needle->http_proxy.host); - if(result) - goto out; - } - if(needle->bits.socksproxy) { - result = Curl_idnconvert_hostname(&needle->socks_proxy.host); + if(network_scheme && data->set.str[STRING_UNIX_SOCKET_PATH]) { + result = Curl_peer_uds_create(needle->origin->scheme, + data->set.str[STRING_UNIX_SOCKET_PATH], + (bool)data->set.abstract_unix_socket, + &needle->via_peer); if(result) goto out; } -#endif - if(needle->bits.conn_to_host) { - result = Curl_idnconvert_hostname(&needle->conn_to_host); +#endif /* USE_UNIX_SOCKETS */ + + if(network_scheme && !needle->via_peer) { + /************************************************************* + * If the `via_peer` is not already set (via UDS above), + * determine if we talk to `conn->origin` directly or use + * `conn->via_peer` using "connect to" and "alt-svc" properties. + *************************************************************/ + result = url_set_conn_peer(data, needle); if(result) goto out; } /************************************************************* * Check whether the host and the "connect to host" are equal. - * Do this after the hostnames have been IDN-converted. + * Do this after the hostnames have been IDN-converted and + * before initializing the proxy. *************************************************************/ - if(needle->bits.conn_to_host && - curl_strequal(needle->conn_to_host.name, needle->host.name)) { - needle->bits.conn_to_host = FALSE; + if(Curl_peer_equal(needle->origin, needle->via_peer)) { + Curl_peer_unlink(&needle->via_peer); } - /************************************************************* - * 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(needle->bits.conn_to_port && - needle->conn_to_port == needle->remote_port) { - needle->bits.conn_to_port = FALSE; +#ifndef CURL_DISABLE_PROXY + /* Going via a unix socket ignores any proxy settings */ + if(network_scheme && + (!needle->via_peer || !needle->via_peer->unix_socket)) { + result = Curl_proxy_init_conn(data, needle); + if(result) + goto out; } +#endif /* CURL_DISABLE_PROXY */ -#ifndef CURL_DISABLE_PROXY - /************************************************************* - * If the "connect to" feature is used with an HTTP proxy, - * we set the tunnel_proxy bit. - *************************************************************/ - if((needle->bits.conn_to_host || needle->bits.conn_to_port) && - needle->bits.httpproxy) - needle->bits.tunnel_proxy = TRUE; -#endif + result = url_set_conn_login(data, needle); /* default credentials */ + if(result) + goto out; /************************************************************* * Setup internals depending on protocol. Needs to be done after @@ -3183,7 +2173,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, needle->bits.tls_enable_alpn = TRUE; } - if(!(needle->scheme->flags & PROTOPT_NONETWORK)) { + if(network_scheme) { /* Setup callbacks for network connections */ needle->recv[FIRSTSOCKET] = Curl_cf_recv; needle->send[FIRSTSOCKET] = Curl_cf_send; @@ -3191,7 +2181,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, 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)) + if(Curl_conn_get_first_peer(needle, FIRSTSOCKET)->unix_socket) needle->transport_wanted = TRNSPRT_UNIX; #endif } @@ -3199,6 +2189,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, out: if(!result) { DEBUGASSERT(needle); + DEBUGASSERT(needle->origin); *pneedle = needle; } else { @@ -3209,6 +2200,118 @@ static CURLcode url_create_needle(struct Curl_easy *data, return result; } +static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data) +{ + CURLcode result = CURLE_OK; + CURLU *uh; + CURLUcode uc; + bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); + uint16_t port_override = data->state.allow_port ? data->set.use_port : 0; + uint32_t scope_id = 0; + + /************************************************************* + * Check input data + *************************************************************/ + if(!Curl_bufref_ptr(&data->state.url)) { + result = CURLE_URL_MALFORMAT; + goto out; + } + + up_free(data); /* cleanup previous leftovers first */ + + /* parse the URL */ + if(use_set_uh) + uh = data->state.uh = curl_url_dup(data->set.uh); + else + uh = data->state.uh = curl_url(); + if(!uh) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + /* Calculate the *real* URL this transfer uses, applying defaults + * where information is missing. */ + if(data->set.str[STRING_DEFAULT_PROTOCOL] && + !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) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + Curl_bufref_set(&data->state.url, url, 0, curl_free); + } + + if(!use_set_uh) { + char *newurl; + 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) | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); + if(uc) { + failf(data, "URL rejected: %s", curl_url_strerror(uc)); + result = Curl_uc_to_curlcode(uc); + goto out; + } + + /* after it was parsed, get the generated normalized version */ + uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY); + if(uc) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + Curl_bufref_set(&data->state.url, newurl, 0, curl_free); + } + +#ifdef USE_IPV6 + scope_id = data->set.scope_id; +#endif + + /* `uh` is now as the connection should use it, probably. */ + result = Curl_peer_from_url(uh, data, port_override, scope_id, + &data->state.up, &data->state.origin); + if(result) + goto out; + /* The origin might get changed when HSTS applies */ + result = hsts_upgrade(data, uh, port_override, scope_id); + if(result) + goto out; + + /* When the transfers initial_origin is not set, this is the initial + * request. Remember this starting point. */ + if(!data->state.initial_origin) + Curl_peer_link(&data->state.initial_origin, data->state.origin); + + uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE); + if(uc) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, + CURLU_GET_EMPTY); + if(uc && (uc != CURLUE_NO_QUERY)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, + CURLU_URLDECODE); + if(uc && (uc != CURLUE_NO_OPTIONS)) { + result = Curl_uc_to_curlcode(uc); + goto out; + } + + result = url_set_data_creds(data, uh); + if(result) + goto out; + +out: + return result; +} + /** * Find an existing connection for the transfer or create a new one. * Returns @@ -3270,7 +2373,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) #endif /* Complete the easy's SSL configuration for connection cache matching */ - result = Curl_ssl_easy_config_complete(data); + result = Curl_ssl_easy_config_complete(data, needle->origin); if(result) goto out; @@ -3303,22 +2406,21 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) infof(data, "Reusing existing %s: connection%s with %s %s", conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", - conn->bits.proxy ? "proxy" : "host", - conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : - conn->http_proxy.host.name ? conn->http_proxy.host.dispname : - conn->host.dispname); + (conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host", + conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname : + conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname : + conn->origin->hostname); #else infof(data, "Reusing existing %s: connection%s with host %s", conn->given->name, tls_upgraded ? " (upgraded to SSL)" : "", - conn->host.dispname); + conn->origin->hostname); #endif } else { /* 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 @@ -3353,7 +2455,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) * 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")); + DEBUGF(curl_mfprintf(stderr, "Error: init connection SSL config\n")); goto out; } /* attach it and no longer own it */ @@ -3402,7 +2504,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) #ifdef CURL_DISABLE_PROXY 0 #else - data->conn->bits.proxy + (data->conn->socks_proxy.peer || data->conn->http_proxy.peer) #endif ; @@ -3419,21 +2521,33 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) { CURLcode result; - struct connectdata *conn; + struct connectdata *conn = NULL; *pconnected = FALSE; /* Set the request to virgin state based on transfer settings */ Curl_req_hard_reset(&data->req, data); + /* Determine the origin of the transfer and what credentials to use */ + result = url_set_data_origin_and_creds(data); + if(result) + goto out; + if(!data->state.origin) { /* just make really sure */ + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + goto out; + } /* Get or create a connection for the transfer. */ result = url_find_or_create_conn(data); conn = data->conn; - if(result) goto out; + if(!data->conn) { /* just make really sure */ + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + goto out; + } - DEBUGASSERT(conn); Curl_pgrsTime(data, TIMER_POSTQUEUE); if(conn->bits.reuse) { if(conn->attached_xfers > 1) @@ -3445,11 +2559,10 @@ CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) *pconnected = TRUE; } else { - result = Curl_conn_setup(data, conn, FIRSTSOCKET, NULL, - CURL_CF_SSL_DEFAULT); + result = Curl_conn_setup(data, conn, FIRSTSOCKET, CURL_CF_SSL_DEFAULT); if(!result) result = Curl_headers_init(data); - CURL_TRC_M(data, "Curl_conn_setup() -> %d", result); + CURL_TRC_M(data, "Curl_conn_setup() -> %d", (int)result); } out: @@ -3505,96 +2618,6 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) #if defined(USE_HTTP2) || defined(USE_HTTP3) -#ifdef USE_NGHTTP2 - -static void priority_remove_child(struct Curl_easy *parent, - struct Curl_easy *child) -{ - struct Curl_data_prio_node **pnext = &parent->set.priority.children; - struct Curl_data_prio_node *pnode = parent->set.priority.children; - - DEBUGASSERT(child->set.priority.parent == parent); - while(pnode && pnode->data != child) { - pnext = &pnode->next; - pnode = pnode->next; - } - - DEBUGASSERT(pnode); - if(pnode) { - *pnext = pnode->next; - curlx_free(pnode); - } - - child->set.priority.parent = 0; - child->set.priority.exclusive = FALSE; -} - -CURLcode Curl_data_priority_add_child(struct Curl_easy *parent, - struct Curl_easy *child, - bool exclusive) -{ - if(child->set.priority.parent) { - priority_remove_child(child->set.priority.parent, child); - } - - if(parent) { - struct Curl_data_prio_node **tail; - struct Curl_data_prio_node *pnode; - - pnode = curlx_calloc(1, sizeof(*pnode)); - if(!pnode) - return CURLE_OUT_OF_MEMORY; - pnode->data = child; - - if(parent->set.priority.children && exclusive) { - /* exclusive: move all existing children underneath the new child */ - struct Curl_data_prio_node *node = parent->set.priority.children; - while(node) { - node->data->set.priority.parent = child; - node = node->next; - } - - tail = &child->set.priority.children; - while(*tail) - tail = &(*tail)->next; - - DEBUGASSERT(!*tail); - *tail = parent->set.priority.children; - parent->set.priority.children = 0; - } - - tail = &parent->set.priority.children; - while(*tail) { - (*tail)->data->set.priority.exclusive = FALSE; - tail = &(*tail)->next; - } - - DEBUGASSERT(!*tail); - *tail = pnode; - } - - child->set.priority.parent = parent; - child->set.priority.exclusive = exclusive; - return CURLE_OK; -} - -#endif /* USE_NGHTTP2 */ - -#ifdef USE_NGHTTP2 -static void data_priority_cleanup(struct Curl_easy *data) -{ - while(data->set.priority.children) { - struct Curl_easy *tmp = data->set.priority.children->data; - priority_remove_child(data, tmp); - if(data->set.priority.parent) - Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE); - } - - if(data->set.priority.parent) - priority_remove_child(data->set.priority.parent, data); -} -#endif - void Curl_data_priority_clear_state(struct Curl_easy *data) { memset(&data->state.priority, 0, sizeof(data->state.priority)); diff --git a/lib/url.h b/lib/url.h index 66baf7017428..ebbe9d53c47f 100644 --- a/lib/url.h +++ b/lib/url.h @@ -25,6 +25,9 @@ ***************************************************************************/ #include "curl_setup.h" +/* Reject URLs exceeding this length */ +#define MAX_URL_LEN 0xffff + /* * Prototypes for library-wide functions */ diff --git a/lib/urlapi.c b/lib/urlapi.c index 103e0a6b57e1..05c79dfb0df4 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -253,12 +253,18 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace) * Parse the login details (username, password and options) from the URL and * strip them out of the hostname * + * @unittest 1675 */ -static CURLUcode parse_hostname_login(struct Curl_URL *u, - const char *login, - size_t len, - unsigned int flags, - size_t *offset) /* to the hostname */ +UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, + const char *login, + size_t len, + unsigned int flags, + size_t *hostname_offset); +UNITTEST CURLUcode parse_hostname_login(struct Curl_URL *u, + const char *login, + size_t len, + unsigned int flags, + size_t *hostname_offset) { CURLUcode ures = CURLUE_OK; CURLcode result; @@ -278,7 +284,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, DEBUGASSERT(login); - *offset = 0; + *hostname_offset = 0; ptr = memchr(login, '@', len); if(!ptr) goto out; @@ -326,7 +332,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, } /* the hostname starts at this offset */ - *offset = ptr - login; + *hostname_offset = ptr - login; return CURLUE_OK; out: @@ -334,9 +340,9 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u, curlx_free(userp); curlx_free(passwdp); curlx_free(optionsp); - u->user = NULL; - u->password = NULL; - u->options = NULL; + curlx_safefree(u->user); + curlx_safefree(u->password); + curlx_safefree(u->options); return ures; } @@ -471,10 +477,17 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, return ipv6_parse(u, hostname, hlen); else { /* letters from the second string are not ok */ - len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%"); + len = strcspn(hostname, " \r\n\t/:#?!@{}[]\\$\'\"^`*<>=;,+&()%|"); if(hlen != len) /* hostname with bad content */ return CURLUE_BAD_HOSTNAME; + else if((hlen >= 2) && + (hostname[hlen - 1] == '.') && (hostname[hlen - 2] == '.')) + /* more than one trailing dot is not allowed */ + return CURLUE_BAD_HOSTNAME; + else if((hlen == 1) && (hostname[0] == '.')) + /* a single dot alone is not allowed */ + return CURLUE_BAD_HOSTNAME; } return CURLUE_OK; } @@ -489,11 +502,13 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname, * Output the "normalized" version of that input string in plain quad decimal * integers. * + * A single dot following the numerical address is accepted and "swallowed" as + * if it was never there. + * * Returns the host type. * * @unittest 1675 */ - UNITTEST int ipv4_normalize(struct dynbuf *host); UNITTEST int ipv4_normalize(struct dynbuf *host) { @@ -510,9 +525,11 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) int rc; curl_off_t l; if(*c == '0') { - if(c[1] == 'x') { + if(Curl_raw_tolower(c[1]) == 'x') { c += 2; /* skip the prefix */ rc = curlx_str_hex(&c, &l, UINT_MAX); + if(rc) + return HOST_NAME; } else rc = curlx_str_octal(&c, &l, UINT_MAX); @@ -520,17 +537,26 @@ UNITTEST int ipv4_normalize(struct dynbuf *host) else rc = curlx_str_number(&c, &l, UINT_MAX); - if(rc) - return HOST_NAME; - - parts[n] = (unsigned int)l; + if(rc) { + if(!n || (rc != STRE_NO_NUM) || *c) + return HOST_NAME; + n--; + } + else + parts[n] = (unsigned int)l; switch(*c) { case '.': - if(n == 3) - return HOST_NAME; - n++; - c++; + if(n == 3) { + if(c[1]) + /* something follows this dot */ + return HOST_NAME; + done = TRUE; + } + else { + n++; + c++; + } break; case '\0': @@ -631,20 +657,24 @@ static CURLUcode parse_authority(struct Curl_URL *u, */ uc = parse_hostname_login(u, auth, authlen, flags, &offset); if(uc) - goto out; + return uc; result = curlx_dyn_addn(host, auth + offset, authlen - offset); if(result) { uc = cc2cu(result); - goto out; + return uc; } uc = parse_port(u, host, has_scheme); if(uc) - goto out; + return uc; if(!curlx_dyn_len(host)) - return CURLUE_NO_HOST; + uc = CURLUE_NO_HOST; + else + uc = urldecode_host(host); + if(uc) + return uc; switch(ipv4_normalize(host)) { case HOST_IPV4: @@ -653,9 +683,7 @@ static CURLUcode parse_authority(struct Curl_URL *u, uc = ipv6_parse(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); break; case HOST_NAME: - uc = urldecode_host(host); - if(!uc) - uc = hostname_check(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); + uc = hostname_check(u, curlx_dyn_ptr(host), curlx_dyn_len(host)); break; case HOST_ERROR: uc = CURLUE_OUT_OF_MEMORY; @@ -665,7 +693,6 @@ static CURLUcode parse_authority(struct Curl_URL *u, break; } -out: return uc; } @@ -855,13 +882,19 @@ UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, path = &url[5]; pathlen = urllen - 5; + /* RFC 8089: file-hier-part = ( "//" auth-path ) / local-path, where + local-path also starts with a "/". So reject anything that does not + start with at least one "/" */ + if(path[0] != '/') + return CURLUE_BAD_FILE_URL; + /* 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] == '/') { + if(path[1] == '/') { /* swallow the two slashes */ const char *ptr = &path[2]; @@ -1006,7 +1039,7 @@ static CURLUcode handle_fragment(CURLU *u, const char *fragment, CURLUcode ures; u->fragment_present = TRUE; if(fraglen > 1) { - /* skip the leading '#' in the copy but include the terminating null */ + /* skip the leading '#' in the copy but include the null-terminator */ if(flags & CURLU_URLENCODE) { struct dynbuf enc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); @@ -1138,8 +1171,7 @@ 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) { - ures = parse_authority(u, hostp, hostlen, flags, &host, - u->scheme != NULL); + ures = parse_authority(u, hostp, hostlen, flags, &host, !!u->scheme); if(!ures && (flags & CURLU_GUESS_SCHEME) && !u->scheme) ures = guess_scheme(u, &host); } @@ -1212,9 +1244,12 @@ static CURLUcode redirect_url(const char *base, const char *relurl, const char *cutoff = NULL; size_t prelen; CURLUcode uc; + /* this can get here with a NULL u->scheme only if asked to use the default + scheme, so allow fallback to that */ + const char *scheme = u->scheme ? u->scheme : DEFAULT_SCHEME; /* protsep points to the start of the hostname, after [scheme]:// */ - const char *protsep = base + strlen(u->scheme) + 3; + const char *protsep = base + strlen(scheme) + 3; DEBUGASSERT(base && relurl && u); /* all set here */ if(!base) return CURLUE_MALFORMED_INPUT; /* should never happen */ @@ -1417,6 +1452,20 @@ static CURLUcode urlget_format(const CURLU *u, CURLUPart what, return CURLUE_OK; } +static CURLUcode file_url(const CURLU *u, char **part, + const char *fragmentsep, + const char *querysep) +{ + char *url = curl_maprintf("file://%s%s%s%s%s", + u->path, querysep, u->query ? u->query : "", + fragmentsep, u->fragment ? u->fragment : ""); + if(!url) + return CURLUE_OUT_OF_MEMORY; + + *part = url; + return CURLUE_OK; +} + static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) { char *url; @@ -1428,11 +1477,8 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) (u->query_present && flags & CURLU_GET_EMPTY)) ? "?" : ""; char portbuf[7]; - if(u->scheme && curl_strequal("file", u->scheme)) { - url = curl_maprintf("file://%s%s%s%s%s", - u->path, querysep, u->query ? u->query : "", - fragmentsep, u->fragment ? u->fragment : ""); - } + if(curl_strequal("file", u->scheme)) + return file_url(u, part, fragmentsep, querysep); else if(!u->host) return CURLUE_NO_HOST; else { @@ -1449,25 +1495,22 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) return CURLUE_NO_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) { + if(h) { + if(!port && (flags & CURLU_DEFAULT_PORT)) { + /* there is no stored port number, but asked to deliver a default one + for the scheme */ curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); port = portbuf; } - } - else if(port) { - /* there is a stored port number, but asked to inhibit if it matches - the default one for the scheme */ - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) + else if(port && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) + /* there is a stored port number, but asked to inhibit if it matches + the default port for the scheme */ port = NULL; + if(!(h->flags & PROTOPT_URLOPTIONS)) + options = NULL; } - if(h && !(h->flags & PROTOPT_URLOPTIONS)) - options = NULL; - if(u->host[0] == '[') { if(u->zoneid) { /* make it '[ host %25 zoneid ]' */ @@ -1691,8 +1734,11 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, and this is a redirect */ uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags); if(!uc) { - /* success, meaning the "" is a fine relative URL, but nothing - changes */ + /* success, meaning the "" is a fine relative URL, and the new URL + inherits scheme/authority/path/query, but not fragment, from the + existing URL (RFC 3986 section 5.2.2) */ + curlx_safefree(u->fragment); + u->fragment_present = FALSE; curlx_free(oldurl); return CURLUE_OK; } @@ -1707,8 +1753,9 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, 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); + 'oldurl'), replace the existing with the new. + Always include "scheme://" to make the URL "complete" */ + uc = curl_url_get(u, CURLUPART_URL, &oldurl, flags& ~CURLU_NO_GUESS_SCHEME); if(uc == CURLUE_OUT_OF_MEMORY) return uc; else if(uc) @@ -1794,6 +1841,126 @@ static bool allowed_in_path(unsigned char x) return FALSE; } +static CURLUcode url_encode_part(struct dynbuf *encp, + const char *part, + bool plusencode, + bool pathmode, + bool equalsencode) +{ + const unsigned char *i; + + for(i = (const unsigned char *)part; *i; i++) { + CURLcode result; + if((*i == ' ') && plusencode) + result = curlx_dyn_addn(encp, "+", 1); + else if(ISUNRESERVED(*i) || + (pathmode && allowed_in_path(*i)) || + ((*i == '=') && equalsencode)) { + if((*i == '=') && equalsencode) + /* only skip the first equals sign */ + equalsencode = FALSE; + result = curlx_dyn_addn(encp, i, 1); + } + else { + unsigned char out[3] = { '%' }; + Curl_hexbyte(&out[1], *i); + result = curlx_dyn_addn(encp, out, 3); + } + if(result) + return cc2cu(result); + } + return CURLUE_OK; +} + +static CURLUcode url_uppercasehex_part(struct dynbuf *encp, + const char *part) +{ + char *p; + CURLcode result = curlx_dyn_add(encp, part); + if(result) + return cc2cu(result); + p = curlx_dyn_ptr(encp); + while(*p) { + /* make sure percent encoded are upper case */ + if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && + (ISLOWER(p[1]) || ISLOWER(p[2]))) { + p[1] = Curl_raw_toupper(p[1]); + p[2] = Curl_raw_toupper(p[2]); + p += 3; + } + else + p++; + } + return CURLUE_OK; +} + +static CURLUcode url_append_query(CURLU *u, struct dynbuf *encp) +{ + /* Append the 'encp' string onto the old query. Add a '&' separator if none + is already present at the end of the existing query */ + + size_t querylen = u->query ? strlen(u->query) : 0; + bool addamperand = querylen && (u->query[querylen - 1] != '&'); + if(querylen) { + struct dynbuf qbuf; + CURLcode result; + const char *newp = curlx_dyn_ptr(encp); + curlx_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH); + + /* add original query */ + result = curlx_dyn_addn(&qbuf, u->query, querylen); + if(!result && addamperand) + /* add ampersand */ + result = curlx_dyn_addn(&qbuf, "&", 1); + if(!result) + /* add new query part */ + result = curlx_dyn_add(&qbuf, newp); + if(result) + goto nomem; + curlx_dyn_free(encp); + curlx_free(u->query); + u->query = curlx_dyn_ptr(&qbuf); + return CURLUE_OK; +nomem: + curlx_dyn_free(encp); + return cc2cu(result); + } + else { + curlx_free(u->query); + u->query = curlx_dyn_ptr(encp); + } + return CURLUE_OK; +} + +static CURLUcode url_sethost(CURLU *u, struct dynbuf *encp, + bool urlencode, + unsigned int flags) +{ + size_t n = curlx_dyn_len(encp); + bool bad = FALSE; + char *newp = curlx_dyn_ptr(encp); + if(!n) + /* an empty hostname is okay if told so */ + bad = (flags & CURLU_NO_AUTHORITY) ? FALSE : TRUE; + else if(!urlencode) { + /* if the hostname part was not URL encoded here, it was set already URL + encoded so we need to decode it to check */ + size_t dlen; + char *decoded = NULL; + CURLcode result = Curl_urldecode(newp, n, &decoded, &dlen, REJECT_CTRL); + if(result || hostname_check(u, decoded, dlen)) + bad = TRUE; + curlx_free(decoded); + } + else if(hostname_check(u, (char *)CURL_UNCONST(newp), n)) + bad = TRUE; + if(bad) { + curlx_dyn_free(encp); + return CURLUE_BAD_HOSTNAME; + } + return CURLUE_OK; +} + CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags) { @@ -1867,8 +2034,9 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } DEBUGASSERT(storep); { - const char *newp; + const char *newp = NULL; struct dynbuf enc; + CURLUcode status; curlx_dyn_init(&enc, (nalloc * 3) + 1 + leadingslash); if(leadingslash && (part[0] != '/')) { @@ -1876,112 +2044,20 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, if(result) return cc2cu(result); } - if(urlencode) { - const unsigned char *i; - - for(i = (const unsigned char *)part; *i; i++) { - CURLcode result; - if((*i == ' ') && plusencode) { - result = curlx_dyn_addn(&enc, "+", 1); - if(result) - return CURLUE_OUT_OF_MEMORY; - } - else if(ISUNRESERVED(*i) || - (pathmode && allowed_in_path(*i)) || - ((*i == '=') && equalsencode)) { - if((*i == '=') && equalsencode) - /* only skip the first equals sign */ - equalsencode = FALSE; - result = curlx_dyn_addn(&enc, i, 1); - if(result) - return cc2cu(result); - } - else { - unsigned char out[3] = { '%' }; - Curl_hexbyte(&out[1], *i); - result = curlx_dyn_addn(&enc, out, 3); - if(result) - return cc2cu(result); - } - } - } - else { - char *p; - CURLcode result = curlx_dyn_add(&enc, part); - if(result) - return cc2cu(result); - p = curlx_dyn_ptr(&enc); - while(*p) { - /* make sure percent encoded are lower case */ - if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) && - (ISUPPER(p[1]) || ISUPPER(p[2]))) { - p[1] = Curl_raw_tolower(p[1]); - p[2] = Curl_raw_tolower(p[2]); - p += 3; - } - else - p++; - } - } - newp = curlx_dyn_ptr(&enc); - - if(appendquery && newp) { - /* Append the 'newp' string onto the old query. Add a '&' separator if - 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] != '&'); - if(querylen) { - struct dynbuf qbuf; - curlx_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH); - - if(curlx_dyn_addn(&qbuf, u->query, querylen)) /* add original query */ - goto nomem; - - if(addamperand) { - if(curlx_dyn_addn(&qbuf, "&", 1)) - goto nomem; - } - if(curlx_dyn_add(&qbuf, newp)) - goto nomem; - curlx_dyn_free(&enc); - curlx_free(*storep); - *storep = curlx_dyn_ptr(&qbuf); - return CURLUE_OK; -nomem: - curlx_dyn_free(&enc); - return CURLUE_OUT_OF_MEMORY; - } - } - - else if(what == CURLUPART_HOST) { - size_t n = curlx_dyn_len(&enc); - if(!n && (flags & CURLU_NO_AUTHORITY)) { - /* Skip hostname check, it is allowed to be empty. */ - } - else { - bool bad = FALSE; - if(!n) - bad = TRUE; /* empty hostname is not okay */ - else if(!urlencode) { - /* 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; - CURLcode result = - Curl_urldecode(newp, n, &decoded, &dlen, REJECT_CTRL); - if(result || hostname_check(u, decoded, dlen)) - bad = TRUE; - curlx_free(decoded); - } - else if(hostname_check(u, (char *)CURL_UNCONST(newp), n)) - bad = TRUE; - if(bad) { - curlx_dyn_free(&enc); - return CURLUE_BAD_HOSTNAME; - } - } + if(urlencode) + status = url_encode_part(&enc, part, plusencode, pathmode, equalsencode); + else + status = url_uppercasehex_part(&enc, part); + if(!status) { + newp = curlx_dyn_ptr(&enc); + + if(appendquery && newp) + return url_append_query(u, &enc); + else if(what == CURLUPART_HOST) + status = url_sethost(u, &enc, urlencode, flags); } + if(status) + return status; curlx_free(*storep); *storep = (char *)CURL_UNCONST(newp); @@ -2001,6 +2077,9 @@ bool Curl_url_same_origin(CURLU *base, CURLU *href) if(href->host) { if(!curl_strequal(base->host, href->host)) return FALSE; + if(!curl_strequal(base->zoneid ? base->zoneid : "", + href->zoneid ? href->zoneid : "")) + return FALSE; if(!curl_strequal(base->port, href->port)) { /* This may still match if only one has an explicit port * and it is the default for the scheme. */ diff --git a/lib/urldata.h b/lib/urldata.h index f35bfee053d0..d4d336d8dbdb 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -55,11 +55,14 @@ #include "asyn.h" #include "cookie.h" +#include "creds.h" #include "psl.h" #include "formdata.h" #include "http_chunks.h" /* for the structs and enum stuff */ #include "hostip.h" #include "hash.h" +#include "peer.h" +#include "proxy.h" #include "splay.h" #include "curlx/dynbuf.h" #include "bufref.h" @@ -67,6 +70,7 @@ #include "request.h" #include "ratelimit.h" #include "netrc.h" +#include "vtls/vtls_config.h" /* On error return, the value of `pnwritten` has no meaning */ typedef CURLcode (Curl_send)(struct Curl_easy *data, /* transfer */ @@ -138,62 +142,6 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) #endif -struct ssl_primary_config { - char *CApath; /* certificate directory (does not work on Windows) */ - char *CAfile; /* certificate to verify peer against */ - char *issuercert; /* optional issuer certificate filename */ - char *clientcert; - char *cipher_list; /* list of ciphers to use */ - char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ - char *signature_algorithms; /* list of signature algorithms to use */ - char *pinned_key; - char *CRLfile; /* CRL to check certificate revocation */ - struct curl_blob *cert_blob; - struct curl_blob *ca_info_blob; - struct curl_blob *issuercert_blob; -#ifdef USE_TLS_SRP - char *username; /* TLS username (for, e.g., SRP) */ - char *password; /* TLS password (for, e.g., SRP) */ -#endif - char *curves; /* list of curves 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 */ - BIT(cache_session); /* cache session or not */ -}; - -struct ssl_config_data { - struct ssl_primary_config primary; - 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 *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 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(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 { - int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ -}; - #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" #endif @@ -201,14 +149,12 @@ struct ssl_general_config { #ifndef CURL_DISABLE_DIGEST_AUTH /* Struct used for Digest challenge-response authentication */ struct digestdata { + struct Curl_creds *creds; + struct Curl_peer *origin; #ifdef USE_WINDOWS_SSPI BYTE *input_token; size_t input_token_len; CtxtHandle *http_context; - /* copy of user/passwd used to make the identity for http_context. - either may be NULL. */ - char *user; - char *passwd; #else char *nonce; char *cnonce; @@ -246,23 +192,12 @@ typedef enum { 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 */ - BIT(proxy_user_passwd); /* user+password for the proxy? */ - BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. - This is implicit when SSL-protocols are used through - proxies, but can also be enabled explicitly by - apps */ - BIT(proxy); /* if set, this transfer is done through a proxy - any type */ + BIT(origin_is_proxy); /* if set, the connection's origin is a proxy */ #endif /* always modify bits.close with the connclose() and connkeep() macros! */ BIT(close); /* if set, we close the connection after this request */ BIT(reuse); /* if set, this is a reused connection */ BIT(altused); /* this is an alt-svc "redirect" */ - BIT(conn_to_host); /* if set, this connection has a "connect to host" - 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); /* 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() */ @@ -278,10 +213,6 @@ struct ConnectBits { EPRT does not work we disable it for the forthcoming requests */ BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */ - BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */ -#endif -#ifndef CURL_DISABLE_NETRC - BIT(netrc); /* name+password provided by netrc */ #endif BIT(bound); /* set true if bind() has already been done on this socket/ connection */ @@ -289,9 +220,6 @@ struct ConnectBits { BIT(multiplex); /* connection is multiplexed */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ -#ifdef USE_UNIX_SOCKETS - BIT(abstract_unix_socket); -#endif BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with accept() */ BIT(parallel_connect); /* set TRUE when a parallel connect attempt has @@ -320,6 +248,8 @@ struct hostname { #define TRNSPRT_QUIC 5 #define TRNSPRT_UNIX 6 +#define TRNSPRT_IS_DGRAM(x) (((x) == TRNSPRT_UDP) || ((x) == TRNSPRT_QUIC)) + struct ip_quadruple { char remote_ip[MAX_IPADR_LEN]; char local_ip[MAX_IPADR_LEN]; @@ -333,14 +263,6 @@ struct ip_quadruple { ((x)->transport == TRNSPRT_UDP) || \ ((x)->transport == TRNSPRT_QUIC)) -struct proxy_info { - struct hostname host; - 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 */ -}; - /* * The connectdata struct contains all fields and variables that should be * unique for an entire connection. @@ -369,19 +291,18 @@ struct connectdata { * the connection is cleaned up (see Curl_hash_add2()).*/ struct Curl_hash meta_hash; - struct hostname host; - 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 */ + /* Who the connection is talking to, ultimately */ + struct Curl_peer *origin; /* connection ultimately talks to this */ + struct Curl_peer *via_peer; /* if set, connection really talks to this */ + struct Curl_peer *origin2; /* origin of SECONDARYSOCKET */ + struct Curl_peer *via_peer2; /* peer of SECONDARYSOCKET */ #ifndef CURL_DISABLE_PROXY struct proxy_info socks_proxy; struct proxy_info http_proxy; #endif - char *user; /* username string, allocated */ - char *passwd; /* password string, allocated */ + struct Curl_creds *creds; /* When connection itself is tied to credentials */ + struct Curl_peer *creds_origin; /* origin tied credentials are for */ char *options; /* options string, allocated */ - char *sasl_authzid; /* authorization identity string, allocated */ - char *oauth_bearer; /* OAUTH2 bearer, allocated */ struct curltime created; /* creation time */ struct curltime lastused; /* when returned to the connection pool as idle */ @@ -425,7 +346,7 @@ struct connectdata { /*************** Request - specific items ************/ #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) - CtxtHandle *sslContext; /* mingw-w64 v9+. MS SDK 7.0A+. */ + CtxtHandle *sslContext; /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ #endif #ifdef USE_NTLM @@ -438,10 +359,6 @@ struct connectdata { curlnegotiate proxy_negotiate_state; #endif -#ifdef USE_UNIX_SOCKETS - char *unix_domain_socket; -#endif - /* When this connection is created, store the conditions for the local end bind. This is stored before the actual bind and before any connection is made and will serve the purpose of being used for comparison reasons so @@ -456,14 +373,8 @@ struct connectdata { #ifdef USE_IPV6 uint32_t scope_id; /* Scope id for IPv6 */ #endif - /* 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) */ 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 @@ -477,14 +388,13 @@ struct connectdata { #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)->socks_proxy.peer ? (c)->socks_proxy.peer->user_hostname : \ + (c)->http_proxy.peer ? (c)->http_proxy.peer->user_hostname : \ + (c)->via_peer ? (c)->via_peer->user_hostname : \ + (c)->origin->user_hostname) #else #define CURL_CONN_HOST_DISPNAME(c) \ - (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \ - (c)->host.dispname + ((c)->via_peer ? (c)->via_peer->user_hostname : (c)->origin->user_hostname) #endif /* The end of connectdata. */ @@ -517,9 +427,6 @@ struct PureInfo { session handle without disturbing information which is still alive, and that might be reused, in the connection pool. */ struct ip_quadruple primary; - int conn_remote_port; /* this is the "remote port", which is the port - number of the used URL, independent of proxy or - not */ const char *conn_scheme; uint32_t conn_protocol; struct curl_certinfo certs; /* info about the certs. Asked for with @@ -604,8 +511,6 @@ struct auth { actual request */ BIT(multipass); /* TRUE if this is not yet authenticated but within the auth multipass negotiation */ - BIT(iestyle); /* TRUE if digest should be done IE-style or FALSE if it - should be RFC compliant */ }; #ifdef USE_NGHTTP2 @@ -620,15 +525,7 @@ struct Curl_data_prio_node { * on the same connection. */ struct Curl_data_priority { -#ifdef USE_NGHTTP2 - /* tree like dependencies only implemented in nghttp2 */ - struct Curl_easy *parent; - struct Curl_data_prio_node *children; -#endif int weight; -#ifdef USE_NGHTTP2 - BIT(exclusive); -#endif }; /* Timers */ @@ -672,11 +569,6 @@ struct urlpieces { char *query; }; -#define CREDS_NONE 0 -#define CREDS_URL 1 /* from URL */ -#define CREDS_OPTION 2 /* set with a CURLOPT_ */ -#define CREDS_NETRC 3 /* found in netrc */ - struct UrlState { /* buffers to store authentication data in, as parsed from input options */ struct curltime keeps_speed; /* for the progress meter really */ @@ -692,13 +584,14 @@ struct UrlState { curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ - /* hostname, port number and protocol of the first (not followed) request. - if set, this should be the hostname that we will sent authorization to, - no else. Used to make Location: following not keep sending user+password. - This is strdup()ed data. */ - char *first_host; - int first_remote_port; - curl_prot_t first_remote_protocol; + /* Origin of the initial (e.g. not followed) request of a transfer. + Credentials from CURLOPT_* are only valid for this origin. + Always set once a transfer starts searching for connections. */ + struct Curl_peer *initial_origin; + /* Current origin of the transfer, changes to origin of follow + * requests. */ + struct Curl_peer *origin; + int os_errno; /* filled in with errno whenever an error occurs */ int requests; /* request counter: redirects + authentication retakes */ #ifdef HAVE_SIGNAL @@ -706,6 +599,7 @@ struct UrlState { void (*prev_signal)(int sig); #endif #ifndef CURL_DISABLE_DIGEST_AUTH + char *envproxy; /* last proxy string used for proxy-related state */ struct digestdata digest; /* state data for host Digest auth */ struct digestdata proxydigest; /* state data for proxy Digest auth */ #endif @@ -787,6 +681,8 @@ struct UrlState { struct store_netrc netrc; #endif + struct Curl_creds *creds; /* Credentials for the origin only */ + /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { @@ -798,14 +694,6 @@ struct UrlState { #ifndef CURL_DISABLE_RTSP char *rtsp_transport; #endif - - /* transfer credentials */ - char *user; - char *passwd; -#ifndef CURL_DISABLE_PROXY - char *proxyuser; - char *proxypasswd; -#endif } aptr; #ifndef CURL_DISABLE_HTTP struct http_negotiation http_neg; @@ -815,14 +703,8 @@ struct UrlState { 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 */ - - /* when curl_easy_perform() is called, the multi handle is "owned" by - the easy handle so curl_easy_cleanup() on such an easy handle will - also close the multi handle! */ - BIT(multi_owned_by_easy); + BIT(really_alive); /* transfer is really alive in multi, passed INIT */ BIT(this_is_a_follow); /* this is a followed Location: request */ BIT(refused_stream); /* this was refused, try again */ BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in. @@ -929,7 +811,7 @@ enum dupstring { STRING_SET_REFERER, /* custom string for the HTTP referer field */ STRING_SET_URL, /* what original URL to work on */ STRING_USERAGENT, /* User-Agent string */ - STRING_SSL_ENGINE, /* name of ssl engine */ + STRING_SSL_ENGINE, /* name of SSL engine */ STRING_USERNAME, /* , if used */ STRING_PASSWORD, /* , if used */ STRING_OPTIONS, /* , if used */ @@ -1177,9 +1059,6 @@ struct UserDefined { struct curl_slist *mail_rcpt; /* linked list of mail recipients */ #endif uint32_t maxconnects; /* Max idle connections in the connection cache */ -#ifdef USE_ECH - int tls_ech; /* TLS ECH configuration */ -#endif short maxredirs; /* maximum no. of http(s) redirects to follow, set to -1 for infinity */ uint16_t expect_100_timeout; /* in milliseconds */ @@ -1194,6 +1073,9 @@ struct UserDefined { #ifndef CURL_DISABLE_TFTP uint16_t tftp_blksize; /* in bytes, 0 means use default */ #endif +#ifdef USE_ECH + uint8_t tls_ech; /* TLS ECH configuration */ +#endif #ifndef CURL_DISABLE_NETRC uint8_t use_netrc; /* enum CURL_NETRC_OPTION values */ #endif @@ -1213,9 +1095,8 @@ struct UserDefined { uint8_t ipver; /* the CURL_IPRESOLVE_* defines in the public header file 0 - whatever, 1 - v2, 2 - v6 */ uint8_t upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */ -#ifdef HAVE_GSSAPI - /* GSS-API credential delegation, see the documentation of - CURLOPT_GSSAPI_DELEGATION */ +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + /* GSS-API/SSPI credential delegation, see CURLOPT_GSSAPI_DELEGATION */ uint8_t gssapi_delegation; #endif uint8_t http_follow_mode; /* follow HTTP redirects */ diff --git a/lib/vauth/cleartext.c b/lib/vauth/cleartext.c index 7976adec9c8f..ca0c9967ff9c 100644 --- a/lib/vauth/cleartext.c +++ b/lib/vauth/cleartext.c @@ -40,24 +40,21 @@ * * Parameters: * - * authzid [in] - The authorization identity. - * authcid [in] - The authentication identity. + * creds [in] - The credentials. * passwd [in] - The password. * out [out] - The result storage. * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, +CURLcode Curl_auth_create_plain_message(struct Curl_creds *creds, struct bufref *out) { size_t len; char *auth; - size_t zlen = (authzid == NULL ? 0 : strlen(authzid)); - size_t clen = strlen(authcid); - size_t plen = strlen(passwd); + size_t zlen = strlen(Curl_creds_sasl_authzid(creds)); + size_t clen = strlen(Curl_creds_user(creds)); + size_t plen = strlen(Curl_creds_passwd(creds)); if((zlen > CURL_MAX_INPUT_LENGTH) || (clen > CURL_MAX_INPUT_LENGTH) || (plen > CURL_MAX_INPUT_LENGTH)) @@ -65,8 +62,10 @@ CURLcode Curl_auth_create_plain_message(const char *authzid, len = zlen + clen + plen + 2; - auth = curl_maprintf("%s%c%s%c%s", authzid ? authzid : "", '\0', - authcid, '\0', passwd); + auth = curl_maprintf("%s%c%s%c%s", + Curl_creds_sasl_authzid(creds), '\0', + Curl_creds_user(creds), '\0', + Curl_creds_passwd(creds)); if(!auth) return CURLE_OUT_OF_MEMORY; Curl_bufref_set(out, auth, len, curl_free); diff --git a/lib/vauth/cram.c b/lib/vauth/cram.c index d3f2d137b779..023c31b58b67 100644 --- a/lib/vauth/cram.c +++ b/lib/vauth/cram.c @@ -47,18 +47,19 @@ * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct bufref *out) { struct HMAC_context *ctxt; unsigned char digest[MD5_DIGEST_LEN]; char *response; + const char *user = Curl_creds_user(creds); + const char *passwd = Curl_creds_passwd(creds); /* Compute the digest using the password as the key */ ctxt = Curl_HMAC_init(&Curl_HMAC_MD5, - (const unsigned char *)passwdp, - curlx_uztoui(strlen(passwdp))); + (const unsigned char *)passwd, + curlx_uztoui(strlen(passwd))); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -73,7 +74,7 @@ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, /* Generate the response */ 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], + user, digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); if(!response) diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 5ecfd60aad6f..1818fc96c7bd 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -36,6 +36,7 @@ #include "curl_sha512_256.h" #include "curlx/strparse.h" #include "rand.h" +#include "escape.h" #ifndef USE_WINDOWS_SSPI #define SESSION_ALGO 1 /* for algos with this bit set */ @@ -163,6 +164,11 @@ static char *auth_digest_string_quoted(const char *s) if(!result) result = curlx_dyn_addn(&out, s, 1); } + else if((*s < ' ') || (*s > 0x7e)) { + unsigned char buf[3] = { '%' }; + Curl_hexbyte(&buf[1], (unsigned char)*s); + result = curlx_dyn_addn(&out, buf, 3); + } else result = curlx_dyn_addn(&out, s, 1); if(result) @@ -231,7 +237,7 @@ static bool auth_digest_get_key_value(const char *chlg, const char *key, static void auth_digest_get_qop_values(const char *options, int *value) { struct Curl_str out; - /* Initialise the output */ + /* Initialize the output */ *value = 0; while(!curlx_str_until(&options, &out, 32, ',')) { @@ -332,13 +338,16 @@ bool Curl_auth_is_digest_supported(void) */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, struct bufref *out) { + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; size_t i; struct MD5_context *ctxt; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); char *response = NULL; unsigned char digest[MD5_DIGEST_LEN]; char HA1_hex[(2 * MD5_DIGEST_LEN) + 1]; @@ -370,7 +379,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return result; /* We only support md5 sessions */ - if(strcmp(algorithm, "md5-sess") != 0) + if(strcmp(algorithm, "md5-sess")) return CURLE_BAD_CONTENT_ENCODING; /* Get the qop-values from the qop-options */ @@ -418,7 +427,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); /* Generate our SPN */ - spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); + spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL); if(!spn) return CURLE_OUT_OF_MEMORY; @@ -517,7 +526,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, if(digest->nonce) before = TRUE; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_digest_cleanup(digest); for(;;) { @@ -666,8 +675,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Parameters: * * data [in] - The session handle. - * userp [in] - The username. - * passwdp [in] - The user's password. + * creds [in] - The credentials * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. @@ -679,8 +687,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, */ static CURLcode auth_create_digest_http_message( struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -689,6 +696,8 @@ static CURLcode auth_create_digest_http_message( CURLcode (*hash)(unsigned char *, const unsigned char *, const size_t)) { CURLcode result; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); unsigned char hashbuf[32]; /* 32 bytes/256 bits */ unsigned char request_digest[65]; unsigned char ha1[65]; /* 64 digits and 1 zero byte */ @@ -835,7 +844,8 @@ static CURLcode auth_create_digest_http_message( if(digest->qop) hashthis = curl_maprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, - digest->nc, digest->cnonce, digest->qop, ha2); + (unsigned int)digest->nc, digest->cnonce, + digest->qop, ha2); else hashthis = curl_maprintf("%s:%s:%s", ha1, digest->nonce, ha2); @@ -900,7 +910,7 @@ static CURLcode auth_create_digest_http_message( nonce_quoted, uri_quoted, digest->cnonce, - digest->nc, + (unsigned int)digest->nc, digest->qop, request_digest); @@ -986,29 +996,28 @@ static CURLcode auth_create_digest_http_message( * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen) { if(digest->algo <= ALGO_MD5SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_md5_to_ascii, Curl_md5it); if(digest->algo <= ALGO_SHA256SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_sha256_to_ascii, Curl_sha256it); #ifdef CURL_HAVE_SHA512_256 if(digest->algo <= ALGO_SHA512_256SESS) - return auth_create_digest_http_message(data, userp, passwdp, + return auth_create_digest_http_message(data, creds, request, uripath, digest, outptr, outlen, auth_digest_sha256_to_ascii, @@ -1031,6 +1040,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, */ void Curl_auth_digest_cleanup(struct digestdata *digest) { + Curl_peer_unlink(&digest->origin); + Curl_creds_unlink(&digest->creds); curlx_safefree(digest->nonce); curlx_safefree(digest->cnonce); curlx_safefree(digest->realm); diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index f0b6780fca6f..305e367e175b 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -28,6 +28,7 @@ #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH) +#include "creds.h" #include "vauth/vauth.h" #include "vauth/digest.h" #include "curlx/multibyte.h" @@ -83,9 +84,8 @@ bool Curl_auth_is_digest_supported(void) */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, struct bufref *out) { CURLcode result = CURLE_OK; @@ -103,6 +103,8 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; /* Ensure we have a valid challenge message */ if(!Curl_bufref_len(chlg)) { @@ -131,15 +133,16 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Generate our SPN */ - spn = Curl_auth_build_spn(service, data->conn->host.name, NULL); + spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL); if(!spn) { curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &identity); + result = Curl_create_sspi_identity(creds->user, creds->passwd, + &identity); if(result) { curlx_free(spn); curlx_free(output_token); @@ -381,8 +384,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -421,16 +423,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* If the user/passwd that was used to make the identity for http_context has changed then delete that context. */ - if((userp && !digest->user) || (!userp && digest->user) || - (passwdp && !digest->passwd) || (!passwdp && digest->passwd) || - (userp && digest->user && Curl_timestrcmp(userp, digest->user)) || - (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { + if(!Curl_creds_same(creds, digest->creds)) { if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); curlx_safefree(digest->http_context); } - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + Curl_creds_unlink(&digest->creds); } if(digest->http_context) { @@ -458,7 +456,8 @@ 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", status); + infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", + (unsigned long)status); Curl_pSecFn->DeleteSecurityContext(digest->http_context); curlx_safefree(digest->http_context); } @@ -473,13 +472,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, unsigned long attrs; TCHAR *spn; - /* free the copy of user/passwd used to make the previous identity */ - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + /* free the credentials used to make the previous identity */ + Curl_creds_unlink(&digest->creds); - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - if(Curl_create_sspi_identity(userp, passwdp, &identity)) { + if(Curl_create_sspi_identity(creds->user, creds->passwd, &identity)) { curlx_free(output_token); return CURLE_OUT_OF_MEMORY; } @@ -499,26 +497,8 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* Use the current Windows user */ p_identity = NULL; - if(userp) { - digest->user = curlx_strdup(userp); - - if(!digest->user) { - curlx_free(output_token); - Curl_sspi_free_identity(p_identity); - return CURLE_OUT_OF_MEMORY; - } - } - - if(passwdp) { - digest->passwd = curlx_strdup(passwdp); - - if(!digest->passwd) { - curlx_free(output_token); - Curl_sspi_free_identity(p_identity); - curlx_safefree(digest->user); - return CURLE_OUT_OF_MEMORY; - } - } + if(creds) + Curl_creds_link(&digest->creds, creds); /* Acquire our credentials handle */ status = Curl_pSecFn->AcquireCredentialsHandle(NULL, @@ -577,11 +557,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* 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, NULL); + 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 || @@ -649,8 +630,8 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) } /* Free the copy of user/passwd used to make the identity for http_context */ - curlx_safefree(digest->user); - curlx_safefree(digest->passwd); + Curl_creds_unlink(&digest->creds); + Curl_peer_unlink(&digest->origin); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */ diff --git a/lib/vauth/gsasl.c b/lib/vauth/gsasl.c index 958f4ffab746..10a83fdb0998 100644 --- a/lib/vauth/gsasl.c +++ b/lib/vauth/gsasl.c @@ -32,6 +32,10 @@ #include +#if GSASL_VERSION_NUMBER < 0x010600 +#error "requires libgsasl 1.6.0+" +#endif + bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, const char *mech, struct gsasldata *gsasl) @@ -47,6 +51,7 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, res = gsasl_client_start(gsasl->ctx, mech, &gsasl->client); if(res != GSASL_OK) { gsasl_done(gsasl->ctx); + gsasl->ctx = NULL; return FALSE; } @@ -54,15 +59,14 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, } CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct gsasldata *gsasl) { #if GSASL_VERSION_NUMBER >= 0x010b00 int res; res = #endif - gsasl_property_set(gsasl->client, GSASL_AUTHID, userp); + gsasl_property_set(gsasl->client, GSASL_AUTHID, creds->user); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { failf(data, "setting AUTHID failed: %s", gsasl_strerror(res)); @@ -73,7 +77,7 @@ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, #if GSASL_VERSION_NUMBER >= 0x010b00 res = #endif - gsasl_property_set(gsasl->client, GSASL_PASSWORD, passwdp); + gsasl_property_set(gsasl->client, GSASL_PASSWORD, creds->passwd); #if GSASL_VERSION_NUMBER >= 0x010b00 if(res != GSASL_OK) { failf(data, "setting PASSWORD failed: %s", gsasl_strerror(res)); diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index 64c735be582a..738ce9a744a8 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -74,9 +74,8 @@ bool Curl_auth_is_gssapi_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -89,9 +88,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, OM_uint32 unused_status; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - - (void)userp; - (void)passwdp; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; if(!krb5->spn) { gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index e7491be022f8..543a6fe284bc 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -79,9 +79,8 @@ bool Curl_auth_is_gssapi_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -97,6 +96,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, SecBufferDesc resp_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; if(!krb5->spn) { /* Generate our SPN */ @@ -128,9 +129,10 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, if(!krb5->credentials) { /* Do we have credentials to use or are we using single sign-on? */ - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); + result = Curl_create_sspi_identity( + creds->user, creds->passwd, &krb5->identity); if(result) return result; @@ -152,8 +154,10 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, krb5->p_identity, NULL, NULL, krb5->credentials, NULL); - if(status != SEC_E_OK) + if(status != SEC_E_OK) { + curlx_safefree(krb5->credentials); return CURLE_LOGIN_DENIED; + } /* Allocate our new context handle */ krb5->context = curlx_calloc(1, sizeof(CtxtHandle)); @@ -287,10 +291,13 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, input_buf[1].pvBuffer = NULL; input_buf[1].cbBuffer = 0; - /* Decrypt the inbound challenge and obtain the qop */ + /* Decrypt the inbound challenge and obtain the qop. The encrypted message + is decrypted in place, overwriting the original contents of its buffer. + The SECBUFFER_DATA receives a pointer to the message in + SECBUFFER_STREAM. */ status = Curl_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); if(status != SEC_E_OK) { - infof(data, "GSSAPI handshake failure (empty security message)"); + infof(data, "GSSAPI handshake failure (decryption failed)"); return CURLE_BAD_CONTENT_ENCODING; } @@ -306,9 +313,6 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, max_size = ((unsigned long)indata[1] << 16) | ((unsigned long)indata[2] << 8) | indata[3]; - /* Free the challenge as it is not required anymore */ - Curl_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); - /* Process the security layer */ if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { infof(data, "GSSAPI handshake failure (invalid security layer)"); @@ -428,15 +432,13 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) /* Free our security context */ if(krb5->context) { Curl_pSecFn->DeleteSecurityContext(krb5->context); - curlx_free(krb5->context); - krb5->context = NULL; + curlx_safefree(krb5->context); } /* Free our credentials handle */ if(krb5->credentials) { Curl_pSecFn->FreeCredentialsHandle(krb5->credentials); - curlx_free(krb5->credentials); - krb5->credentials = NULL; + curlx_safefree(krb5->credentials); } /* Free our identity */ diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 1e485ed34d6f..2803c05d9f0e 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -361,8 +361,8 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, ntlm->flags = 0; if((type2len < 32) || - (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) || - (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) { + memcmp(type2, NTLMSSP_SIGNATURE, 8) || + memcmp(type2 + 8, type2_marker, sizeof(type2_marker))) { /* This was not a good enough type-2 message */ infof(data, "NTLM handshake failure (bad type-2 message)"); return CURLE_BAD_CONTENT_ENCODING; @@ -421,9 +421,8 @@ static void unicodecpy(unsigned char *dest, const char *src, size_t length) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out) @@ -442,6 +441,8 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, (*) -> Optional */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; size_t size; char *ntlmbuf; @@ -453,12 +454,11 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, size_t domoff = hostoff + hostlen; /* This is 0: remember that host and domain are empty */ (void)data; - (void)userp; - (void)passwdp; + (void)creds; (void)service; (void)host; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_cleanup_ntlm(ntlm); ntlmbuf = curl_maprintf(NTLMSSP_SIGNATURE "%c" @@ -542,8 +542,7 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out) { @@ -579,6 +578,8 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, /* The fixed hostname we provide, in order to not leak our real local host name. Copy the name used by Firefox. */ static const char host[] = "WORKSTATION"; + const char *userp = Curl_creds_user(creds); + const char *passwdp = Curl_creds_passwd(creds); const char *user; const char *domain = ""; size_t hostoff = 0; diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 4c41eb21f4e6..2e98e86e5f3d 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -76,9 +76,8 @@ bool Curl_auth_is_ntlm_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out) @@ -88,8 +87,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, SecBufferDesc type_1_desc; SECURITY_STATUS status; unsigned long attrs; + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; - /* Clean up any former leftovers and initialise to defaults */ + /* Clean up any former leftovers and initialize to defaults */ Curl_auth_cleanup_ntlm(ntlm); /* Query the security package for NTLM */ @@ -111,11 +112,12 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, if(!ntlm->output_token) return CURLE_OUT_OF_MEMORY; - if(userp && *userp) { + if(Curl_creds_has_user(creds)) { CURLcode result; /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); + result = Curl_create_sspi_identity( + creds->user, creds->passwd, &ntlm->identity); if(result) return result; @@ -137,8 +139,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, ntlm->p_identity, NULL, NULL, ntlm->credentials, NULL); - if(status != SEC_E_OK) + if(status != SEC_E_OK) { + curlx_safefree(ntlm->credentials); return CURLE_LOGIN_DENIED; + } /* Allocate our new context handle */ ntlm->context = curlx_calloc(1, sizeof(CtxtHandle)); @@ -227,8 +231,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out) { @@ -240,8 +243,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, SECURITY_STATUS status; unsigned long attrs; - (void)passwdp; - (void)userp; + (void)creds; /* Setup the type-2 "input" security buffer */ type_2_desc.ulVersion = SECBUFFER_VERSION; @@ -252,7 +254,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len); #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* ssl context comes from schannel. + /* 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 does not pass the authentication (401 response). @@ -296,7 +298,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, &attrs, NULL); if(status != SEC_E_OK) { infof(data, "NTLM handshake failure (type-3 message): Status=0x%08lx", - status); + (unsigned long)status); if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; @@ -325,15 +327,13 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) /* Free our security context */ if(ntlm->context) { Curl_pSecFn->DeleteSecurityContext(ntlm->context); - curlx_free(ntlm->context); - ntlm->context = NULL; + curlx_safefree(ntlm->context); } /* Free our credentials handle */ if(ntlm->credentials) { Curl_pSecFn->FreeCredentialsHandle(ntlm->credentials); - curlx_free(ntlm->credentials); - ntlm->credentials = NULL; + curlx_safefree(ntlm->credentials); } /* Free our identity */ diff --git a/lib/vauth/oauth2.c b/lib/vauth/oauth2.c index 4541d1d551bb..f597114638a5 100644 --- a/lib/vauth/oauth2.c +++ b/lib/vauth/oauth2.c @@ -47,21 +47,22 @@ * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, +CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_creds *creds, const char *host, const long port, - const char *bearer, struct bufref *out) { char *oauth; /* Generate the message */ if(port == 0 || port == 80) - oauth = curl_maprintf("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", + Curl_creds_user(creds), host, + Curl_creds_oauth_bearer(creds)); else oauth = curl_maprintf("n,a=%s,\1host=%s\1port=%ld\1auth=Bearer %s\1\1", - user, host, port, bearer); + Curl_creds_user(creds), host, port, + Curl_creds_oauth_bearer(creds)); if(!oauth) return CURLE_OUT_OF_MEMORY; @@ -83,12 +84,13 @@ CURLcode Curl_auth_create_oauth_bearer_message(const char *user, * * Returns CURLE_OK on success. */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, +CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_creds *creds, struct bufref *out) { /* Generate the message */ - char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); + char *xoauth = curl_maprintf("user=%s\1auth=Bearer %s\1\1", + Curl_creds_user(creds), + Curl_creds_oauth_bearer(creds)); if(!xoauth) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index 38bb4c1422c4..869a27fdc983 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -70,9 +70,8 @@ bool Curl_auth_is_spnego_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego) @@ -90,8 +89,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, struct gss_channel_bindings_struct chan; #endif - (void)user; - (void)password; + (void)creds; if(nego->context && nego->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server @@ -105,6 +103,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; /* Generate our SPN */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; char *spn = Curl_auth_build_spn(service, NULL, host); if(!spn) return CURLE_OUT_OF_MEMORY; diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 1baf59320a37..b7d82c04ddfb 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -78,9 +78,8 @@ bool Curl_auth_is_spnego_supported(void) * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego) @@ -105,6 +104,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->spn) { /* Generate our SPN */ + const char *service = Curl_creds_has_sasl_service(creds) ? + Curl_creds_sasl_service(creds) : default_service; nego->spn = Curl_auth_build_spn(service, host, NULL); if(!nego->spn) return CURLE_OUT_OF_MEMORY; @@ -133,9 +134,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, if(!nego->credentials) { /* Do we have credentials to use or are we using single sign-on? */ - if(user && *user) { + if(Curl_creds_has_user(creds)) { /* Populate our identity structure */ - result = Curl_create_sspi_identity(user, password, &nego->identity); + result = Curl_create_sspi_identity(creds->user, creds->passwd, + &nego->identity); if(result) return result; @@ -157,8 +159,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, SECPKG_CRED_OUTBOUND, NULL, nego->p_identity, NULL, NULL, nego->credentials, NULL); - if(nego->status != SEC_E_OK) + if(nego->status != SEC_E_OK) { + curlx_safefree(nego->credentials); return CURLE_AUTH_ERROR; + } /* Allocate our new context handle */ nego->context = curlx_calloc(1, sizeof(CtxtHandle)); @@ -189,7 +193,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, chlg_buf[0].cbBuffer = curlx_uztoul(chlglen); #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* ssl context comes from Schannel. + /* 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 does not pass the authentication (401 response). @@ -223,15 +227,20 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, resp_buf.cbBuffer = curlx_uztoul(nego->token_max); /* Generate our challenge-response message */ - nego->status = - 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); + { + DWORD sspi_flags = ISC_REQ_CONFIDENTIALITY; + if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG) + sspi_flags |= ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH; + nego->status = + Curl_pSecFn->InitializeSecurityContext(nego->credentials, + chlg ? nego->context : NULL, + nego->spn, + sspi_flags, + 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 */ curlx_free(chlg); @@ -313,15 +322,13 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) /* Free our security context */ if(nego->context) { Curl_pSecFn->DeleteSecurityContext(nego->context); - curlx_free(nego->context); - nego->context = NULL; + curlx_safefree(nego->context); } /* Free our credentials handle */ if(nego->credentials) { Curl_pSecFn->FreeCredentialsHandle(nego->credentials); - curlx_free(nego->credentials); - nego->credentials = NULL; + curlx_safefree(nego->credentials); } /* Free our identity */ diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index a00078fce193..3259556e5f78 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -24,6 +24,7 @@ #include "curl_setup.h" #include "vauth/vauth.h" +#include "creds.h" #include "curlx/multibyte.h" #include "url.h" @@ -111,15 +112,16 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, * * Returns TRUE on success; otherwise FALSE. */ -bool Curl_auth_user_contains_domain(const char *user) +bool Curl_auth_user_contains_domain(struct Curl_creds *creds) { bool valid = FALSE; - if(user && *user) { + if(Curl_creds_has_user(creds)) { /* Check we have a domain name or UPN present */ - const char *p = strpbrk(user, "\\/@"); + const char *p = strpbrk(creds->user, "\\/@"); - valid = (p != NULL && p > user && p < user + strlen(user) - 1); + valid = p && (p > creds->user) && + (p < (creds->user + strlen(creds->user) - 1)); } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else @@ -133,17 +135,18 @@ bool Curl_auth_user_contains_domain(const char *user) /* * Curl_auth_allowed_to_host() tells if authentication, cookies or other - * "sensitive data" can (still) be sent to this host. + * "sensitive data" can be sent to the connection's origin. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data) { - struct connectdata *conn = data->conn; - return !data->state.this_is_a_follow || - data->set.allow_auth_to_other_hosts || - (data->state.first_host && - curl_strequal(data->state.first_host, conn->host.name) && - (data->state.first_remote_port == conn->remote_port) && - (data->state.first_remote_protocol == conn->scheme->protocol)); + return Curl_auth_allowed_to_origin(data, data->state.origin); +} + +bool Curl_auth_allowed_to_origin(struct Curl_easy *data, + struct Curl_peer *origin) +{ + return data->set.allow_auth_to_other_hosts || + Curl_peer_equal(data->state.initial_origin, origin); } #ifdef USE_NTLM diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 279da60be2cb..0f82f929453d 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -30,7 +30,9 @@ #include "urldata.h" struct Curl_easy; +struct Curl_creds; struct connectdata; +struct Curl_peer; #ifndef CURL_DISABLE_DIGEST_AUTH struct digestdata; @@ -58,6 +60,8 @@ struct gsasldata; * "sensitive data" can (still) be sent to this host. */ bool Curl_auth_allowed_to_host(struct Curl_easy *data); +bool Curl_auth_allowed_to_origin(struct Curl_easy *data, + struct Curl_peer *origin); /* This is used to build an SPN string */ #ifndef USE_WINDOWS_SSPI @@ -69,12 +73,10 @@ TCHAR *Curl_auth_build_spn(const char *service, const char *host, #endif /* This is used to test if the user contains a Windows domain name */ -bool Curl_auth_user_contains_domain(const char *user); +bool Curl_auth_user_contains_domain(struct Curl_creds *creds); /* This is used to generate a PLAIN cleartext message */ -CURLcode Curl_auth_create_plain_message(const char *authzid, - const char *authcid, - const char *passwd, +CURLcode Curl_auth_create_plain_message(struct Curl_creds *creds, struct bufref *out); /* This is used to generate a LOGIN cleartext message */ @@ -86,8 +88,7 @@ void Curl_auth_create_external_message(const char *user, struct bufref *out); #ifndef CURL_DISABLE_DIGEST_AUTH /* This is used to generate a CRAM-MD5 response message */ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct bufref *out); /* This is used to evaluate if DIGEST is supported */ @@ -96,9 +97,8 @@ bool Curl_auth_is_digest_supported(void); /* This is used to generate a base64 encoded DIGEST-MD5 response message */ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, const struct bufref *chlg, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, struct bufref *out); /* This is used to decode an HTTP DIGEST challenge message */ @@ -107,8 +107,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* This is used to generate an HTTP DIGEST response message */ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, @@ -140,8 +139,7 @@ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, struct gsasldata *gsasl); /* This is used to start a gsasl method */ CURLcode Curl_auth_gsasl_start(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct gsasldata *gsasl); /* This is used to process and generate a new SASL token */ @@ -197,9 +195,8 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); /* This is used to generate a base64 encoded NTLM type-1 message */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, struct ntlmdata *ntlm, struct bufref *out); @@ -211,8 +208,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, /* This is used to generate a base64 encoded NTLM type-3 message */ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, + struct Curl_creds *creds, struct ntlmdata *ntlm, struct bufref *out); @@ -221,15 +217,13 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, #endif /* USE_NTLM */ /* This is used to generate a base64 encoded OAuth 2.0 message */ -CURLcode Curl_auth_create_oauth_bearer_message(const char *user, +CURLcode Curl_auth_create_oauth_bearer_message(struct Curl_creds *creds, const char *host, const long port, - const char *bearer, struct bufref *out); /* This is used to generate a base64 encoded XOAuth 2.0 message */ -CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, +CURLcode Curl_auth_create_xoauth_bearer_message(struct Curl_creds *creds, struct bufref *out); #ifdef USE_KERBEROS5 @@ -260,9 +254,8 @@ bool Curl_auth_is_gssapi_supported(void); /* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token message */ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, - const char *userp, - const char *passwdp, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const bool mutual_auth, const struct bufref *chlg, @@ -324,15 +317,13 @@ struct negotiatedata { BIT(havemultiplerequests); }; -struct negotiatedata * -Curl_auth_nego_get(struct connectdata *conn, bool proxy); +struct negotiatedata *Curl_auth_nego_get(struct connectdata *conn, bool proxy); /* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge message */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, - const char *user, - const char *password, - const char *service, + struct Curl_creds *creds, + const char *default_service, const char *host, const char *chlg64, struct negotiatedata *nego); diff --git a/lib/version.c b/lib/version.c index b3b0a46abbb1..299caee9fb7d 100644 --- a/lib/version.c +++ b/lib/version.c @@ -388,7 +388,7 @@ static int idn_present(curl_version_info_data *info) (void)info; return TRUE; #else - return info->libidn != NULL; + return !!info->libidn; #endif } #endif @@ -491,6 +491,9 @@ static const struct feat features_table[] = { #ifdef USE_NTLM FEATURE("NTLM", NULL, CURL_VERSION_NTLM), #endif +#ifdef USE_PROXY_HTTP3 + FEATURE("proxy-HTTP3", NULL, 0), +#endif #ifdef USE_LIBPSL FEATURE("PSL", NULL, CURL_VERSION_PSL), #endif diff --git a/lib/vquic/capsule.c b/lib/vquic/capsule.c new file mode 100644 index 000000000000..4bbdae3ace44 --- /dev/null +++ b/lib/vquic/capsule.c @@ -0,0 +1,301 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_PROXY) && !defined(CURL_DISABLE_HTTP) + +#ifdef HAVE_ARPA_INET_H +#include /* for htons() */ +#endif + +#include +#include "urldata.h" +#include "curlx/dynbuf.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "bufq.h" +#include "vquic/capsule.h" + + +/** + * Convert 64-bit value from network byte order to host byte order + */ +static uint64_t capsule_ntohll(uint64_t value) +{ +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + return value; +#elif (defined(__GNUC__) || defined(__clang__)) && \ + defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + return __builtin_bswap64(value); +#else + union { + uint64_t u64; + uint32_t u32[2]; + } src, dst; + + src.u64 = value; + dst.u32[0] = ntohl(src.u32[1]); + dst.u32[1] = ntohl(src.u32[0]); + return dst.u64; +#endif +} + +/** + * Encode a variable-length integer into a plain buffer. + * @param buf Output buffer (must have at least 8 bytes) + * @param value Value to encode (must be <= 0x3FFFFFFFFFFFFFFF) + * @return Number of bytes written + */ +static size_t capsule_encode_varint_buf(uint8_t *buf, uint64_t value) +{ + DEBUGASSERT(value <= 0x3FFFFFFFFFFFFFFF); + + if(value <= 0x3F) { + buf[0] = (uint8_t)value; + return 1; + } + else if(value <= 0x3FFF) { + uint16_t encoded = (uint16_t)value & 0x3FFF; + encoded = ntohs(encoded | 0x4000); + memcpy(buf, &encoded, 2); + return 2; + } + else if(value <= 0x3FFFFFFF) { + uint32_t encoded = (uint32_t)value & 0x3FFFFFFF; + encoded = ntohl(encoded | 0x80000000); + memcpy(buf, &encoded, 4); + return 4; + } + else { + uint64_t encoded = (uint64_t)value & 0x3FFFFFFFFFFFFFFF; + encoded = capsule_ntohll(encoded | 0xC000000000000000); + memcpy(buf, &encoded, 8); + return 8; + } +} + +static CURLcode capsule_peek_u8(struct bufq *recvbufq, + size_t offset, + uint8_t *pbyte) +{ + const unsigned char *peek = NULL; + size_t peeklen = 0; + + if(!Curl_bufq_peek_at(recvbufq, offset, &peek, &peeklen) || !peeklen) + return CURLE_AGAIN; + *pbyte = peek[0]; + return CURLE_OK; +} + +static CURLcode capsule_decode_varint_at(struct bufq *recvbufq, + size_t offset, + uint64_t *pvalue, + size_t *pconsumed) +{ + uint8_t first_byte, byte; + uint64_t value; + size_t nbytes; + size_t i; + CURLcode result; + + result = capsule_peek_u8(recvbufq, offset, &first_byte); + if(result) + return result; + + nbytes = (size_t)1 << (first_byte >> 6); /* 1, 2, 4 or 8 bytes */ + value = first_byte & 0x3F; + + for(i = 1; i < nbytes; ++i) { + result = capsule_peek_u8(recvbufq, offset + i, &byte); + if(result) + return result; + value = (value << 8) | byte; + } + + *pvalue = value; + *pconsumed = nbytes; + return CURLE_OK; +} + +/** + * Write the capsule header (type + varint length + context ID) into `hdr`. + * @param hdr Output buffer (must be >= HTTP_CAPSULE_HEADER_MAX_SIZE) + * @param hdrlen Size of `hdr` in bytes + * @param payload_len Length of the UDP payload that follows + * @return Number of header bytes written, or 0 on error + * + * @unittest 3400 + */ +UNITTEST size_t capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len); +UNITTEST size_t capsule_encap_udp_hdr(uint8_t *hdr, size_t hdrlen, + size_t payload_len) +{ + size_t off = 0; + DEBUGASSERT(hdrlen >= HTTP_CAPSULE_HEADER_MAX_SIZE); + if(hdrlen < HTTP_CAPSULE_HEADER_MAX_SIZE) + return 0; + hdr[off++] = 0; /* capsule type: HTTP Datagram */ + off += capsule_encode_varint_buf(hdr + off, (uint64_t)payload_len + 1); + hdr[off++] = 0; /* context ID */ + return off; +} + +CURLcode Curl_capsule_encap_udp_datagram(struct bufq *q, + const void *buf, size_t blen) +{ + CURLcode result; + uint8_t hdr[HTTP_CAPSULE_HEADER_MAX_SIZE]; + size_t hdr_len, nwritten; + + hdr_len = capsule_encap_udp_hdr(hdr, sizeof(hdr), blen); + DEBUGASSERT(hdr_len); + if(!hdr_len) + return CURLE_FAILED_INIT; + + result = Curl_bufq_write(q, hdr, hdr_len, &nwritten); + if(!result && (nwritten != hdr_len)) + return CURLE_WRITE_ERROR; + if(!result) { + result = Curl_bufq_write(q, buf, blen, &nwritten); + if(!result && (nwritten != blen)) + return CURLE_WRITE_ERROR; + } + if(result == CURLE_AGAIN) + return CURLE_WRITE_ERROR; + return result; +} + +size_t Curl_capsule_process_udp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *recvbufq, + unsigned char *buf, size_t len, + CURLcode *err) +{ + const unsigned char *context_id, *capsule_type; + size_t read_size, varint_len; + uint64_t capsule_length; + size_t offset, payload_len; + size_t bytes_read = 0; + CURLcode result = CURLE_OK; + + if(!len) { + *err = CURLE_BAD_FUNCTION_ARGUMENT; + return 0; + } + + if(Curl_bufq_is_empty(recvbufq)) { + *err = CURLE_AGAIN; + return 0; + } + + if(!Curl_bufq_peek(recvbufq, &capsule_type, &read_size) || !read_size) { + *err = CURLE_AGAIN; + return 0; + } + + if(capsule_type[0]) { + infof(data, "Error! Invalid capsule type: %d", capsule_type[0]); + Curl_bufq_skip(recvbufq, 1); + *err = CURLE_RECV_ERROR; + return 0; + } + + offset = 1; + result = capsule_decode_varint_at(recvbufq, offset, &capsule_length, + &varint_len); + if(result == CURLE_AGAIN) { + *err = CURLE_AGAIN; + return 0; + } + else if(result) { + *err = CURLE_RECV_ERROR; + return 0; + } + offset += varint_len; + + if(!Curl_bufq_peek_at(recvbufq, offset, &context_id, &read_size) || + !read_size) { + *err = CURLE_AGAIN; + return 0; + } + + if(*context_id) { + infof(data, "Error! Invalid context ID: %02x", *context_id); + Curl_bufq_skip(recvbufq, offset + 1); + *err = CURLE_RECV_ERROR; + return 0; + } + offset += 1; + + if(!capsule_length) { + infof(data, "Error! Invalid capsule length: 0"); + Curl_bufq_skip(recvbufq, offset); + *err = CURLE_RECV_ERROR; + return 0; + } + if(capsule_length - 1 >= (uint64_t)SIZE_MAX) { + infof(data, "Error! Capsule length too large: %" CURL_FORMAT_CURL_OFF_T, + (curl_off_t)capsule_length); + *err = CURLE_RECV_ERROR; + return 0; + } + payload_len = (size_t)(capsule_length - 1); + + if(Curl_bufq_len(recvbufq) < offset + payload_len) { + *err = CURLE_AGAIN; + return 0; + } + + if(payload_len > len) { + infof(data, "UDP payload does not fit destination buffer: %zu > %zu", + payload_len, len); + Curl_bufq_skip(recvbufq, offset + payload_len); + *err = CURLE_RECV_ERROR; + return 0; + } + + Curl_bufq_skip(recvbufq, offset); + if(!payload_len) { + *err = CURLE_OK; + return 0; + } + result = Curl_bufq_read(recvbufq, buf, payload_len, &bytes_read); + if(result || (bytes_read != payload_len)) { + infof(data, "Error! Read less than expected %zu %zu", + payload_len, bytes_read); + *err = CURLE_RECV_ERROR; + return 0; + } + + if(cf && data) { + CURL_TRC_CF(data, cf, "Processed UDP capsule raw: size=%zu " + "length_left %zu", payload_len, Curl_bufq_len(recvbufq)); + } + *err = CURLE_OK; + return bytes_read; +} + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ diff --git a/lib/vquic/capsule.h b/lib/vquic/capsule.h new file mode 100644 index 000000000000..7861e6fd739f --- /dev/null +++ b/lib/vquic/capsule.h @@ -0,0 +1,70 @@ +#ifndef HEADER_CURL_CAPSULE_H +#define HEADER_CURL_CAPSULE_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" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include "curlx/dynbuf.h" +#include "bufq.h" + +/* HTTP Capsule constants */ +#define HTTP_CAPSULE_HEADER_MAX_SIZE 10 + +/* HTTP Capsule function prototypes */ + +/** + * Encapsulate UDP payload into HTTP Datagram capsule format + * @param q the bufq to write the capsule to + * @param buf Payload buffer + * @param blen Payload buffer length + * @return CURLE_OK on success, error code on failure + */ +CURLcode Curl_capsule_encap_udp_datagram(struct bufq *q, + const void *buf, size_t blen); + +struct Curl_easy; +struct Curl_cfilter; + +/** + * Process one UDP capsule from buffer into raw datagram payload bytes. + * @param cf Connection filter + * @param data Easy handle + * @param recvbufq Buffer queue containing capsule data + * @param buf Output buffer for one datagram payload + * @param len Size of output buffer in bytes + * @param err Error code output + * @return Number of payload bytes written. Check `err` for status. + */ +size_t Curl_capsule_process_udp_raw(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *recvbufq, + unsigned char *buf, size_t len, + CURLcode *err); + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#endif /* HEADER_CURL_CAPSULE_H */ diff --git a/lib/vquic/cf-capsule.c b/lib/vquic/cf-capsule.c new file mode 100644 index 000000000000..3ff7827bba93 --- /dev/null +++ b/lib/vquic/cf-capsule.c @@ -0,0 +1,306 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_PROXY) && !defined(CURL_DISABLE_HTTP) + +#include "urldata.h" +#include "cfilters.h" +#include "curl_trc.h" +#include "bufq.h" +#include "select.h" +#include "vquic/capsule.h" +#include "vquic/cf-capsule.h" + +/* send/recv buffer: 4 chunks of 16KB = 64KB, enough for large datagrams */ +#define CAPSULE_RECV_CHUNKS 4 +#define CAPSULE_SEND_CHUNKS 4 +#define CAPSULE_CHUNK_SIZE (16 * 1024) + +struct cf_capsule_ctx { + struct bufq recvbuf; + struct bufq sendbuf; +}; + +static void cf_capsule_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + (void)data; + if(ctx) { + Curl_bufq_free(&ctx->recvbuf); + Curl_bufq_free(&ctx->sendbuf); + curlx_safefree(ctx); + } +} + +static CURLcode cf_capsule_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + if(cf->next) { + CURLcode result = cf->next->cft->do_connect(cf->next, data, done); + if(!result && *done) + cf->connected = TRUE; + return result; + } + *done = FALSE; + return CURLE_OK; +} + +static CURLcode cf_capsule_flush(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + size_t nwritten; + + if(Curl_bufq_is_empty(&ctx->sendbuf)) + return CURLE_OK; + + result = Curl_cf_send_bufq(cf->next, data, &ctx->sendbuf, NULL, 0, + &nwritten); + if(result) { + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "flush send buffer(%zu) -> EAGAIN", + Curl_bufq_len(&ctx->sendbuf)); + } + return result; + } + return Curl_bufq_is_empty(&ctx->sendbuf) ? CURLE_OK : CURLE_AGAIN; +} + +static CURLcode cf_capsule_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *buf, size_t len, + bool eos, size_t *pnwritten) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + CURLcode result; + + (void)eos; + *pnwritten = 0; + + if(Curl_bufq_is_full(&ctx->sendbuf)) { + result = cf_capsule_flush(cf, data); + if(result) + return result; + } + + /* encapsulate new payload into a capsule */ + result = Curl_capsule_encap_udp_datagram(&ctx->sendbuf, buf, len); + if(result) + return result; + + result = cf_capsule_flush(cf, data); + if(result == CURLE_AGAIN) { + /* Could not send it (or all), report success nevertheless as we + * have the payload buffered now and will flush it later. */ + result = CURLE_OK; + } + + if(!result) + *pnwritten = len; + return result; +} + +static CURLcode cf_capsule_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + size_t *pnread) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + CURLcode result; + size_t nread; + + *pnread = 0; + + /* fill our receive buffer from the filter below */ + while(!Curl_bufq_is_full(&ctx->recvbuf)) { + result = Curl_cf_recv_bufq(cf->next, data, &ctx->recvbuf, 0, &nread); + if(result == CURLE_AGAIN) + break; + if(result) + return result; + if(!nread) + break; + } + + /* try to extract a complete capsule datagram */ + *pnread = Curl_capsule_process_udp_raw(cf, data, &ctx->recvbuf, + (unsigned char *)buf, len, + &result); + return result; +} + +static bool cf_capsule_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + + if(ctx && !Curl_bufq_is_empty(&ctx->recvbuf)) + return TRUE; + return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +static CURLcode cf_capsule_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_FLUSH: + result = cf_capsule_flush(cf, data); + break; + default: + break; + } + return result; +} + +static CURLcode cf_capsule_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + + (void)pres2; + switch(query) { + case CF_QUERY_NEED_FLUSH: { + if(!Curl_bufq_is_empty(&ctx->sendbuf)) { + *pres1 = TRUE; + return CURLE_OK; + } + break; + } + default: + break; + } + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; +} + +static CURLcode cf_capsule_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_capsule_ctx *ctx = cf->ctx; + + if(!Curl_bufq_is_empty(&ctx->sendbuf)) { + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + if(sock != CURL_SOCKET_BAD) + return Curl_pollset_add_out(data, ps, sock); + } + return CURLE_OK; +} + +static CURLcode cf_capsule_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + CURLcode result = CURLE_OK; + + if(!cf->connected || cf->shutdown) { + *done = TRUE; + } + else { + result = cf_capsule_flush(cf, data); + *done = !result; + if(result == CURLE_AGAIN) + result = CURLE_OK; + } + return result; +} + +struct Curl_cftype Curl_cft_capsule = { + "CAPSULE", + 0, + 0, + cf_capsule_destroy, + cf_capsule_connect, + cf_capsule_shutdown, + cf_capsule_adjust_pollset, + cf_capsule_data_pending, + cf_capsule_send, + cf_capsule_recv, + cf_capsule_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_capsule_query, +}; + +static CURLcode cf_capsule_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct connectdata *conn) +{ + struct Curl_cfilter *cf = NULL; + struct cf_capsule_ctx *ctx; + CURLcode result; + + (void)data; + (void)conn; + *pcf = NULL; + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + Curl_bufq_init2(&ctx->recvbuf, CAPSULE_CHUNK_SIZE, CAPSULE_RECV_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + Curl_bufq_init2(&ctx->sendbuf, CAPSULE_CHUNK_SIZE, CAPSULE_SEND_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + + result = Curl_cf_create(&cf, &Curl_cft_capsule, ctx); + +out: + *pcf = (!result) ? cf : NULL; + if(result && ctx) { + Curl_bufq_free(&ctx->recvbuf); + Curl_bufq_free(&ctx->sendbuf); + curlx_free(ctx); + } + return result; +} + +CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_capsule_create(&cf, data, cf_at->conn); + if(!result) + Curl_conn_cf_insert_after(cf_at, cf); + return result; +} + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ diff --git a/lib/vquic/cf-capsule.h b/lib/vquic/cf-capsule.h new file mode 100644 index 000000000000..437c9681b6cc --- /dev/null +++ b/lib/vquic/cf-capsule.h @@ -0,0 +1,40 @@ +#ifndef HEADER_CURL_CF_CAPSULE_H +#define HEADER_CURL_CF_CAPSULE_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" + +#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) + +/* Insert a capsule protocol filter after `cf_at` in the filter chain. + * The capsule filter encapsulates/decapsulates UDP datagrams using + * the HTTP Datagram capsule format (RFC 9297). */ +CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data); + +extern struct Curl_cftype Curl_cft_capsule; + +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ + +#endif /* HEADER_CURL_CF_CAPSULE_H */ diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/cf-ngtcp2-cmn.c similarity index 52% rename from lib/vquic/curl_ngtcp2.c rename to lib/vquic/cf-ngtcp2-cmn.c index 8cf3886d2241..e1ca18cf69c8 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/cf-ngtcp2-cmn.c @@ -24,12 +24,12 @@ #include "curl_setup.h" #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) + #include -#include #ifdef USE_OPENSSL #include -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) #include #elif defined(OPENSSL_QUIC_API2) #include @@ -45,6 +45,8 @@ #include "vtls/wolfssl.h" #endif +#include + #include "urldata.h" #include "url.h" #include "uint-hash.h" @@ -60,6 +62,7 @@ #include "curlx/dynbuf.h" #include "http1.h" #include "select.h" +#include "sockaddr.h" #include "transfer.h" #include "bufref.h" #include "vquic/vquic.h" @@ -67,34 +70,7 @@ #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_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) - -/* 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 - -/* 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 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_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) +#include "vquic/cf-ngtcp2-cmn.h" /* * Store ngtcp2 version info in this buffer. @@ -107,144 +83,7 @@ void Curl_ngtcp2_ver(char *p, size_t len) ng2->version_str, ht3->version_str); } -struct cf_ngtcp2_ctx { - struct cf_quic_ctx q; - struct ssl_peer peer; - struct curl_tls_ctx tls; -#ifdef OPENSSL_QUIC_API2 - ngtcp2_crypto_ossl_ctx *ossl_ctx; -#endif - ngtcp2_path connected_path; - ngtcp2_conn *qconn; - ngtcp2_cid dcid; - ngtcp2_cid scid; - uint32_t version; - ngtcp2_settings settings; - ngtcp2_transport_params transport_params; - ngtcp2_ccerr last_error; - ngtcp2_crypto_conn_ref conn_ref; - struct cf_call_data call_data; - nghttp3_conn *h3conn; - nghttp3_settings h3settings; - 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 dynbuf scratch; /* temp buffer for header construction */ - struct uint_hash streams; /* hash `data->mid` to `h3_stream_ctx` */ - uint64_t used_bidi_streams; /* bidi streams we have opened */ - uint64_t max_bidi_streams; /* max bidi streams we can open */ - size_t earlydata_max; /* max amount of early data supported by - server on session reuse */ - size_t earlydata_skip; /* sending bytes to skip when earlydata - is accepted by peer */ - CURLcode tls_vrfy_result; /* result of TLS peer verification */ - int qlogfd; - BIT(initialized); - BIT(tls_handshake_complete); /* TLS handshake is done */ - BIT(use_earlydata); /* Using 0RTT data */ - BIT(earlydata_accepted); /* 0RTT was accepted by server */ - BIT(shutdown_started); /* queued shutdown packets */ -}; - -/* 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 - -static void h3_stream_hash_free(unsigned int id, void *stream); - -static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx) -{ - DEBUGASSERT(!ctx->initialized); - ctx->qlogfd = -1; - ctx->version = NGTCP2_PROTO_VER_MAX; - Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, - H3_STREAM_POOL_SPARES); - curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); - Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); - ctx->initialized = TRUE; -} - -static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx) -{ - if(ctx && ctx->initialized) { - Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); - Curl_bufcp_free(&ctx->stream_bufcp); - curlx_dyn_free(&ctx->scratch); - Curl_uint32_hash_destroy(&ctx->streams); - Curl_ssl_peer_cleanup(&ctx->peer); - } - curlx_free(ctx); -} - -static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - 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 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; - - rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); - if(!rp || !rp->max_idle_timeout) { - 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_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"); - } - else { - 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 %" 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, - struct pkt_io_ctx *pktx); -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx); - -/** - * All about the H3 internals of a stream - */ -struct h3_stream_ctx { - 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" */ - 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) +void Curl_cf_ngtcp2_h3_stream_ctx_free(struct h3_stream_ctx *stream) { Curl_bufq_free(&stream->sendbuf); Curl_h1_req_parse_free(&stream->h1); @@ -255,165 +94,65 @@ 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_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - - if(!data) - return CURLE_FAILED_INIT; - - if(stream) - return CURLE_OK; - - stream = curlx_calloc(1, sizeof(*stream)); - if(!stream) - return CURLE_OUT_OF_MEMORY; - - stream->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_uint32_hash_set(&ctx->streams, data->mid, stream)) { - h3_stream_ctx_free(stream); - return CURLE_OUT_OF_MEMORY; - } - - 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; + Curl_cf_ngtcp2_h3_stream_ctx_free((struct h3_stream_ctx *)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) +static bool cf_ngtcp2_h3_err_is_fatal(int code) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - DEBUGASSERT(data); - DEBUGASSERT(stream); - if(!stream->closed && ctx->qconn && ctx->h3conn) { - CURLcode result; - - nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); - ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); - stream->closed = TRUE; - (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, - NGHTTP3_H3_REQUEST_CANCELLED); - result = cf_progress_egress(cf, data, NULL); - if(result) - CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", - stream->id, result); - } + return (NGHTTP3_ERR_FATAL >= code) || + (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); } -static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) +void Curl_cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - (void)cf; - if(stream) { - CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); - cf_ngtcp2_stream_close(cf, data, stream); - Curl_uint32_hash_remove(&ctx->streams, data->mid); - if(!Curl_uint32_hash_count(&ctx->streams)) - cf_ngtcp2_setup_keep_alive(cf, data); + if(!ctx->last_error.error_code) { + ngtcp2_ccerr_set_application_error(&ctx->last_error, + nghttp3_err_infer_quic_app_error_code(code), NULL, 0); } + if(cf_ngtcp2_h3_err_is_fatal(code)) + Curl_cf_ngtcp2_cmn_conn_close(cf, data); } -struct pkt_io_ctx { - struct Curl_cfilter *cf; - struct Curl_easy *data; - ngtcp2_tstamp ts; - ngtcp2_path_storage ps; -}; - -static void pktx_update_time(struct Curl_easy *data, - struct pkt_io_ctx *pktx, - struct Curl_cfilter *cf) +CURLcode Curl_cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + cf_ngtcp2_init_h3_conn *init_h3_conn_cb) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - const struct curltime *pnow = Curl_pgrs_now(data); - - vquic_ctx_update_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); + DEBUGASSERT(!ctx->initialized); + ctx->qlogfd = -1; + ctx->tunnel_inbuf = NULL; + ctx->tunnel_inbuf_len = 0; + ctx->version = NGTCP2_PROTO_VER_MAX; + Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, + H3_STREAM_POOL_SPARES); + curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER); + Curl_uint32_hash_init(&ctx->streams, 63, h3_stream_hash_free); + ctx->init_h3_conn_cb = init_h3_conn_cb; + ctx->initialized = TRUE; + return Curl_vquic_tls_peer_init(origin, peer, sslc, &ctx->ssl_peer); } -static void pktx_init(struct pkt_io_ctx *pktx, - struct Curl_cfilter *cf, - struct Curl_easy *data) +void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx) { - 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); - vquic_ctx_set_time(&ctx->q, pnow); - pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + - ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); + if(ctx && ctx->initialized) { + Curl_vquic_tls_cleanup(&ctx->tls); + vquic_ctx_free(&ctx->q); + Curl_bufcp_free(&ctx->stream_bufcp); + curlx_dyn_free(&ctx->scratch); + Curl_uint32_hash_destroy(&ctx->streams); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); + curlx_safefree(ctx->tunnel_inbuf); + ctx->tunnel_inbuf_len = 0; + if(ctx->qlogfd != -1) { + curlx_close(ctx->qlogfd); + ctx->qlogfd = -1; + } + } } -static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data); - static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) { struct Curl_cfilter *cf = conn_ref->user_data; @@ -424,11 +163,8 @@ static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) #ifdef DEBUG_NGTCP2 static void quic_printf(void *user_data, const char *fmt, ...) { - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - - (void)ctx; /* need an easy handle to infof() message */ va_list ap; + (void)user_data; va_start(ap, fmt); curl_mvfprintf(stderr, fmt, ap); va_end(ap); @@ -454,7 +190,7 @@ static void qlog_callback(void *user_data, uint32_t flags, static void quic_settings(struct cf_ngtcp2_ctx *ctx, struct Curl_easy *data, - struct pkt_io_ctx *pktx) + struct cf_ngtcp2_io_ctx *pktx) { ngtcp2_settings *s = &ctx->settings; ngtcp2_transport_params *t = &ctx->transport_params; @@ -490,10 +226,12 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, } } -static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, - struct Curl_easy *data); +#if defined(_MSC_VER) && defined(_DLL) +#pragma warning(push) +#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ +#endif -static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) +static int cb_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) { struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; @@ -504,24 +242,26 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) data = CF_DATA_CURRENT(cf); DEBUGASSERT(data); if(!ctx || !data) - return NGHTTP3_ERR_CALLBACK_FAILURE; + return NGTCP2_ERR_CALLBACK_FAILURE; ctx->handshake_at = *Curl_pgrs_now(data); ctx->tls_handshake_complete = TRUE; Curl_vquic_report_handshake(&ctx->tls, cf, data); ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, - data, &ctx->peer); + data, &ctx->ssl_peer); + if(ctx->tls_vrfy_result) + return NGTCP2_ERR_CALLBACK_FAILURE; + #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); + ", initial_max_data=%" PRIu64 "]", + curlx_ptimediff_ms(&ctx->handshake_at, &ctx->started_at), + rp->max_udp_payload_size, rp->initial_max_data); } #endif @@ -558,58 +298,12 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) return 0; } -static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data); - -static bool cf_ngtcp2_err_is_fatal(int code) -{ - return (NGTCP2_ERR_FATAL >= code) || - (NGTCP2_ERR_DROP_CONN == code) || - (NGTCP2_ERR_IDLE_CLOSE == code); -} - -static void cf_ngtcp2_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) +static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, + int64_t stream_id, uint64_t offset, + const uint8_t *buf, size_t buflen, + void *user_data, void *stream_user_data) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - if(!ctx->last_error.error_code) { - if(NGTCP2_ERR_CRYPTO == code) { - ngtcp2_ccerr_set_tls_alert(&ctx->last_error, - ngtcp2_conn_get_tls_alert(ctx->qconn), - NULL, 0); - } - else { - ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); - } - } - if(cf_ngtcp2_err_is_fatal(code)) - cf_ngtcp2_conn_close(cf, data); -} - -static bool cf_ngtcp2_h3_err_is_fatal(int code) -{ - return (NGHTTP3_ERR_FATAL >= code) || - (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code); -} - -static void cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, - struct Curl_easy *data, int code) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - if(!ctx->last_error.error_code) { - ngtcp2_ccerr_set_application_error(&ctx->last_error, - nghttp3_err_infer_quic_app_error_code(code), NULL, 0); - } - if(cf_ngtcp2_h3_err_is_fatal(code)) - cf_ngtcp2_conn_close(cf, data); -} - -static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, - 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 Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; nghttp3_ssize rc; uint64_t nconsumed; @@ -687,7 +381,7 @@ static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, 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); + Curl_cf_ngtcp2_h3_err_set(cf, data, rv); return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -811,7 +505,8 @@ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, } #ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ -static int cb_get_new_connection_id2(ngtcp2_conn *tconn, ngtcp2_cid *cid, +static int cb_get_new_connection_id2( + ngtcp2_conn *tconn, ngtcp2_cid *cid, struct ngtcp2_stateless_reset_token *token, size_t cidlen, void *user_data) { CURLcode result; @@ -844,23 +539,18 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, DEBUGASSERT(ctx); DEBUGASSERT(data); - if(ctx && data && !ctx->h3conn) { - if(init_ngh3_conn(cf, data)) + if(ctx && data && !ctx->h3conn && ctx->init_h3_conn_cb) { + if(ctx->init_h3_conn_cb(cf, data, ctx)) return NGTCP2_ERR_CALLBACK_FAILURE; } return 0; } -#if defined(_MSC_VER) && defined(_DLL) -#pragma warning(push) -#pragma warning(disable:4232) /* MSVC extension, dllimport identity */ -#endif - static ngtcp2_callbacks ng_callbacks = { ngtcp2_crypto_client_initial_cb, NULL, /* recv_client_initial */ ngtcp2_crypto_recv_crypto_data_cb, - cf_ngtcp2_handshake_completed, + cb_ngtcp2_handshake_completed, NULL, /* recv_version_negotiation */ ngtcp2_crypto_encrypt_cb, ngtcp2_crypto_decrypt_cb, @@ -912,994 +602,756 @@ static ngtcp2_callbacks ng_callbacks = { #pragma warning(pop) #endif -/** - * Connection maintenance like timeouts on packet ACKs etc. are done by us, not - * the OS like for TCP. POLL events on the socket therefore are not - * sufficient. - * ngtcp2 tells us when it wants to be invoked again. We handle that via - * the `Curl_expire()` mechanisms. - */ -static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct pkt_io_ctx local_pktx; - ngtcp2_tstamp expiry; - - if(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; - } - else { - pktx_update_time(data, pktx, cf); - } - - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - if(expiry != UINT64_MAX) { - if(expiry <= pktx->ts) { - CURLcode result; - int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); - if(rv) { - failf(data, "ngtcp2_conn_handle_expiry returned error: %s", - ngtcp2_strerror(rv)); - cf_ngtcp2_err_set(cf, data, rv); - return CURLE_SEND_ERROR; - } - result = cf_progress_ingress(cf, data, pktx); - if(result) - return result; - result = cf_progress_egress(cf, data, pktx); - if(result) - return result; - /* ask again, things might have changed */ - expiry = ngtcp2_conn_get_expiry(ctx->qconn); - } - - if(expiry > pktx->ts) { - ngtcp2_duration timeout = expiry - pktx->ts; - if(timeout % NGTCP2_MILLISECONDS) { - timeout += NGTCP2_MILLISECONDS; - } - Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), - EXPIRE_QUIC); - } - } - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) +static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - bool want_recv, want_send; - CURLcode result = CURLE_OK; - - if(!ctx->qconn) - return CURLE_OK; - - Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); - if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) - want_send = TRUE; - - if(want_recv || want_send) { - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct cf_call_data save; - bool c_exhaust, s_exhaust; - - 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)); - s_exhaust = want_send && stream && stream->id >= 0 && - stream->quic_flow_blocked; - want_recv = (want_recv || c_exhaust || s_exhaust); - want_send = (!s_exhaust && want_send) || - !Curl_bufq_is_empty(&ctx->q.sendbuf); - - result = Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send); - CF_DATA_RESTORE(cf, save); - } - return result; +#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 } -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) +#ifdef USE_OPENSSL +/* The "new session" callback must return zero if the session can be removed + * or non-zero if the session has been put into the session cache. + */ +static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { - 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_id; + struct Curl_cfilter *cf; + struct cf_ngtcp2_ctx *ctx; + struct Curl_easy *data; + ngtcp2_crypto_conn_ref *cref; - /* we might be called by nghttp3 after we already cleaned up */ - if(!stream) - return 0; + cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); + cf = cref ? cref->user_data : NULL; + ctx = cf ? cf->ctx : NULL; + data = cf ? CF_DATA_CURRENT(cf) : NULL; + if(cf && data && ctx) { + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; +#ifdef HAVE_OPENSSL_EARLYDATA + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; - stream->closed = TRUE; - stream->error3 = app_error_code; - if(stream->error3 != NGHTTP3_H3_NO_ERROR) { - stream->reset = TRUE; - stream->send_closed = TRUE; - CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, - stream->id, stream->error3); - } - else { - CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } +#endif + Curl_ossl_add_session(cf, data, ctx->ssl_peer.scache_key, ssl_sessionid, + SSL_version(ssl), "h3", quic_tp, quic_tp_len); } - Curl_multi_mark_dirty(data); return 0; } +#endif /* USE_OPENSSL */ -static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - const char *buf, size_t blen, bool eos) -{ - /* 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, "[%" PRId64 "] error %d writing %zu " - "bytes of headers", stream->id, stream->xfer_result, blen); - } -} +#ifdef USE_GNUTLS -static void h3_xfer_write_resp(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - const char *buf, size_t blen, bool eos) +#ifdef CURLVERBOSE +static const char *gtls_hs_msg_name(int mtype) { - /* 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, "[%" PRId64 "] error %d writing %zu bytes of data", - stream->id, stream->xfer_result, blen); - } + switch(mtype) { + case 1: + return "ClientHello"; + case 2: + return "ServerHello"; + case 4: + return "SessionTicket"; + case 8: + return "EncryptedExtensions"; + case 11: + return "Certificate"; + case 13: + return "CertificateRequest"; + case 15: + return "CertificateVerify"; + case 20: + return "Finished"; + case 24: + return "KeyUpdate"; + case 254: + return "MessageHash"; } + return "Unknown"; } +#endif -static void cf_ngtcp2_upd_rx_win(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream) +static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, + unsigned when, unsigned int incoming, + const gnutls_datum_t *msg) { - 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; + ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session); + struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; + struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; - if(wanted_win > cur_win) { - uint64_t delta = wanted_win - cur_win; + (void)msg; + (void)incoming; + if(when && cf && ctx) { /* after message has been processed */ + struct Curl_easy *data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(!data) + return 0; + CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]", + incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); + switch(htype) { + case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; - 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); + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } + (void)Curl_gtls_cache_session(cf, data, ctx->ssl_peer.scache_key, + session, 0, "h3", quic_tp, quic_tp_len); + break; + } + default: + break; } } + return 0; } +#endif /* USE_GNUTLS */ -static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, - const uint8_t *buf, size_t blen, - void *user_data, void *stream_user_data) +#ifdef USE_WOLFSSL +static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) { - 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)stream3_id; - - if(!stream) - return NGHTTP3_ERR_CALLBACK_FAILURE; - - h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE); + ngtcp2_crypto_conn_ref *conn_ref = wolfSSL_get_app_data(ssl); + struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - 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; + DEBUGASSERT(cf); + if(cf && session) { + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + DEBUGASSERT(data); + if(data && ctx) { + ngtcp2_ssize tplen; + uint8_t tpbuf[256]; + unsigned char *quic_tp = NULL; + size_t quic_tp_len = 0; - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64, - stream->id, blen, stream->rx_offset_max - stream->rx_offset); - cf_ngtcp2_upd_rx_win(cf, data, stream); + tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, + sizeof(tpbuf)); + if(tplen < 0) + CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", + ngtcp2_strerror((int)tplen)); + else { + quic_tp = (unsigned char *)tpbuf; + quic_tp_len = (size_t)tplen; + } + (void)Curl_wssl_cache_session(cf, data, ctx->ssl_peer.scache_key, + session, wolfSSL_version(ssl), + "h3", quic_tp, quic_tp_len); + } + } return 0; } +#endif /* USE_WOLFSSL */ -static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, - size_t consumed, void *user_data, - void *stream_user_data) +static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, + struct Curl_easy *data, + void *user_data) { - 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; + struct curl_tls_ctx *ctx = 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; +#ifdef USE_OPENSSL +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) + if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) + != 0) { + failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#elif defined(OPENSSL_QUIC_API2) + /* nothing to do */ +#else + if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); + return CURLE_FAILED_INIT; + } +#endif /* !OPENSSL_IS_AWSLC && !OPENSSL_IS_BORINGSSL */ + 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. + */ + SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, + SSL_SESS_CACHE_CLIENT | + SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb); } - return 0; -} - -static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, - int fin, void *user_data, void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_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_id; - (void)fin; - (void)cf; - 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"), - (bool)stream->closed); +#elif defined(USE_GNUTLS) + if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { + failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); + return CURLE_FAILED_INIT; + } + 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); + } - 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; +#elif defined(USE_WOLFSSL) + if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) { + failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); + return CURLE_FAILED_INIT; } - Curl_multi_mark_dirty(data); - return 0; + 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); + } +#endif + return CURLE_OK; } -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) +static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct alpn_spec *alpns, + struct Curl_ssl_session *scs, + bool *do_early_data) { - struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_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) { - - result = Curl_http_decode_status(&stream->status_code, - (const char *)h3val.base, h3val.len); - if(result) - return NGHTTP3_ERR_CALLBACK_FAILURE; - curlx_dyn_reset(&ctx->scratch); - result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 ")); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, - (const char *)h3val.base, h3val.len); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); - 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, "[%" PRId64 "] status: %s", - stream_id, curlx_dyn_ptr(&ctx->scratch)); - if(result) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } + *do_early_data = FALSE; +#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) + ctx->earlydata_max = scs->earlydata_max; +#endif +#ifdef USE_GNUTLS + ctx->earlydata_max = + gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); +#endif +#ifdef USE_WOLFSSL +#ifdef WOLFSSL_EARLY_DATA + ctx->earlydata_max = scs->earlydata_max; +#else + ctx->earlydata_max = 0; +#endif /* WOLFSSL_EARLY_DATA */ +#endif +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ + (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)) + if(!ctx->earlydata_max) { + CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); } - else { - /* store as an HTTP1-style header */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", - stream_id, (int)h3name.len, h3name.base, - (int)h3val.len, h3val.base); - curlx_dyn_reset(&ctx->scratch); - result = curlx_dyn_addn(&ctx->scratch, - (const char *)h3name.base, h3name.len); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, STRCONST(": ")); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, - (const char *)h3val.base, h3val.len); - if(!result) - result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n")); - if(!result) - h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), - curlx_dyn_len(&ctx->scratch), FALSE); + else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { + CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); } - return 0; -} - -static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, - uint64_t app_error_code, void *user_data, - void *stream_user_data) -{ - struct Curl_cfilter *cf = user_data; - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rv; - (void)conn; - (void)stream_user_data; - - rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, - app_error_code); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGHTTP3_ERR_CALLBACK_FAILURE; + else if(!scs->quic_tp || !scs->quic_tp_len) { + CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data"); } - - return 0; + else { + int rv; + rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( + ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len); + if(rv) + CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " + "parameters: %s", ngtcp2_strerror(rv)); + else if(ctx->init_h3_conn_cb) { + infof(data, "SSL session allows %zu bytes of early data, " + "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); + result = ctx->init_h3_conn_cb(cf, data, ctx); + if(!result) { + ctx->use_earlydata = TRUE; + cf->connected = TRUE; + *do_early_data = TRUE; + } + } + else { /* h3_conn_init set, assume done */ + ctx->use_earlydata = TRUE; + cf->connected = TRUE; + *do_early_data = TRUE; + } + } +#else /* not supported in the TLS backend */ + (void)data; + (void)ctx; + (void)scs; + (void)alpns; +#endif + return result; } -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) +/* + * Might be called twice for happy eyeballs. + */ +static CURLcode cf_connect_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) { - struct Curl_cfilter *cf = user_data; struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; + int rc; int rv; - (void)conn; + CURLcode result; + const struct Curl_sockaddr_ex *sockaddr = NULL; + int qfd; + static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; - rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, - app_error_code); - CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); - if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { - return NGHTTP3_ERR_CALLBACK_FAILURE; - } + DEBUGASSERT(ctx->initialized); + ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; - return 0; -} + ctx->scid.datalen = NGTCP2_MAX_CIDLEN; + result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); + if(result) + return result; -static nghttp3_callbacks ngh3_callbacks = { - cb_h3_acked_req_body, /* acked_stream_data */ - cb_h3_stream_close, - cb_h3_recv_data, - cb_h3_deferred_consume, - NULL, /* begin_headers */ - cb_h3_recv_header, - cb_h3_end_headers, - NULL, /* begin_trailers */ - cb_h3_recv_header, - NULL, /* end_trailers */ - cb_h3_stop_sending, - NULL, /* end_stream */ - cb_h3_reset_stream, - NULL, /* shutdown */ - NULL, /* recv_settings (deprecated) */ -#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ - NULL, /* recv_origin */ - NULL, /* end_origin */ - NULL, /* rand */ -#endif -#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ - NULL, /* recv_settings2 */ -#endif -}; + (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); + ctx->qlogfd = qfd; /* -1 if failure above */ + quic_settings(ctx, data, pktx); -static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; - int rc; + result = vquic_ctx_init(data, &ctx->q); + if(result) + return result; - if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { - failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3"); - return CURLE_QUIC_CONNECT_ERROR; + /* Query socket and remote address from sub-chain */ + if(Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL)) { + /* No direct socket - must be tunneled QUIC (CONNECT-UDP through proxy) */ + ctx->q.sockfd = CURL_SOCKET_BAD; + } + + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket - get local address for ngtcp2 */ + 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) + return CURLE_QUIC_CONNECT_ERROR; + + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); + + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; + + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; } + else { + /* Tunneled QUIC (e.g. CONNECT-UDP): get remote address + from the connected filter below */ + const struct Curl_sockaddr_ex *remote = NULL; + if(cf->next->cft->query(cf->next, data, CF_QUERY_REMOTE_ADDR, NULL, + CURL_UNCONST(&remote))) + return CURLE_QUIC_CONNECT_ERROR; + if(!remote) + return CURLE_QUIC_CONNECT_ERROR; + + memset(&ctx->q.local_addr, 0, sizeof(ctx->q.local_addr)); + switch(remote->family) { + case AF_INET: + ((struct sockaddr_in *)&ctx->q.local_addr)->sin_family = AF_INET; + ctx->q.local_addrlen = sizeof(struct sockaddr_in); + break; +#ifdef USE_IPV6 + case AF_INET6: + ((struct sockaddr_in6 *)&ctx->q.local_addr)->sin6_family = AF_INET6; + ctx->q.local_addrlen = sizeof(struct sockaddr_in6); + break; +#endif + default: + return CURLE_QUIC_CONNECT_ERROR; + } - nghttp3_settings_default(&ctx->h3settings); - - rc = nghttp3_conn_client_new(&ctx->h3conn, - &ngh3_callbacks, - &ctx->h3settings, - Curl_nghttp3_mem(), - cf); - if(rc) { - failf(data, "error creating nghttp3 connection instance"); - return CURLE_OUT_OF_MEMORY; - } + ngtcp2_addr_init(&ctx->connected_path.local, + (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&ctx->connected_path.remote, + &remote->curl_sa_addr, + (socklen_t)remote->addrlen); - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 control stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } + rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, + &ctx->connected_path, + NGTCP2_PROTO_VER_V1, &ng_callbacks, + &ctx->settings, &ctx->transport_params, + Curl_ngtcp2_mem(), cf); + if(rc) + return CURLE_QUIC_CONNECT_ERROR; - rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); - if(rc) { - failf(data, "error binding HTTP/3 control stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; + ctx->conn_ref.get_conn = get_conn; + ctx->conn_ref.user_data = cf; } - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 qpack encoding stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; - } + result = Curl_vquic_tls_init(&ctx->tls, cf, data, + &ctx->ssl_peer, &ALPN_SPEC_H3, + cf_ngtcp2_tls_ctx_setup, &ctx->tls, + &ctx->conn_ref, + cf_ngtcp2_on_session_reuse); + if(result) + return result; - rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); - if(rc) { - failf(data, "error creating HTTP/3 qpack decoding stream: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; +#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2) + if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) { + failf(data, "ngtcp2_crypto_ossl_ctx_new failed"); + return CURLE_FAILED_INIT; } - - rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, - qpack_dec_stream_id); - if(rc) { - failf(data, "error binding HTTP/3 qpack streams: %s", - ngtcp2_strerror(rc)); - return CURLE_QUIC_CONNECT_ERROR; + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx); + if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) { + failf(data, "ngtcp2_crypto_ossl_configure_client_session failed"); + return CURLE_FAILED_INIT; } +#elif defined(USE_OPENSSL) + SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); +#elif defined(USE_GNUTLS) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); +#elif defined(USE_WOLFSSL) + ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl); +#else +#error "ngtcp2 TLS backend not defined" +#endif - return CURLE_OK; -} + ngtcp2_ccerr_default(&ctx->last_error); -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) { - 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 %" PRId64 " was closed cleanly, but before " - "getting all response header fields, treated as error", - stream->id); - return CURLE_HTTP3; - } return CURLE_OK; } -/* incoming data frames on the h3 stream */ -static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t blen, size_t *pnread) +CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct cf_call_data save; - struct pkt_io_ctx pktx; CURLcode result = CURLE_OK; - int i; + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; - (void)ctx; - (void)buf; - NOVERBOSE((void)blen); + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx); - DEBUGASSERT(ctx->qconn); - DEBUGASSERT(ctx->h3conn); - *pnread = 0; - - /* handshake verification failed in callback, do not recv anything */ - if(ctx->tls_vrfy_result) { - result = ctx->tls_vrfy_result; - goto denied; + /* Connect the sub-chain */ + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, done); + if(result || !*done) + return result; } - pktx_init(&pktx, cf, data); + *done = FALSE; - if(!stream || ctx->shutdown_started) { - result = CURLE_RECV_ERROR; - goto out; + if(cf_ngtcp2_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex, ctx->ssl_peer.peer)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; } - cf_ngtcp2_upd_rx_win(cf, data, stream); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + CF_DATA_SAVE(save, cf, data); - /* 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); + if(!ctx->qconn) { + ctx->started_at = *Curl_pgrs_now(data); + result = cf_connect_start(cf, data, &pktx); + if(result) goto out; - } - - if(!i && cf_progress_ingress(cf, data, &pktx)) { - result = CURLE_RECV_ERROR; + if(cf->connected) { + *done = TRUE; goto out; } + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + /* we do not expect to be able to recv anything yet */ + goto out; } - result = CURLE_AGAIN; - -out: - 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; -} + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; -static int cb_h3_acked_req_body(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_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = stream_user_data; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - size_t skiplen; + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + if(result) + goto out; - (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; + if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { + result = ctx->tls_vrfy_result; + if(!result) { + CURL_TRC_CF(data, cf, "peer verified"); + cf->connected = TRUE; + *done = TRUE; } } - 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_ngtcp2_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; +out: + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; + if(ctx->qconn && + ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && + ngtcp2_conn_in_draining_period(ctx->qconn)) { + const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn); - 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; + result = CURLE_COULDNT_CONNECT; + if(cerr) { + CURL_TRC_CF(data, cf, "connect error, type=%d, code=%" PRIu64, + (int)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; + } + } } - DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ } - if(nwritten > 0 && stream->upload_left != -1) - stream->upload_left -= nwritten; +#ifdef CURLVERBOSE + if(result) { + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket - get IP info for error reporting */ + struct ip_quadruple ip; - /* 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; + 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)); + } } - else if(!nwritten) { - /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", stream->id); - return NGHTTP3_ERR_WOULDBLOCK; +#endif + if(!result && ctx->qconn) { + result = Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx); } - - CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%d vecs%s with %zd (buffered=%zu, left=%" FMT_OFF_T ")", - stream->id, (int)nvecs, - *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", - nwritten, Curl_bufq_len(&stream->sendbuf), - stream->upload_left); - return (nghttp3_ssize)nvecs; + if(result || *done) + CURL_TRC_CF(data, cf, "connect -> %d, done=%d", (int)result, *done); + CF_DATA_RESTORE(cf, save); + return result; } -static CURLcode h3_stream_open(struct Curl_cfilter *cf, - struct Curl_easy *data, - const uint8_t *buf, size_t len, - size_t *pnwritten) +CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = NULL; - int64_t sid; - struct dynhds h2_headers; - size_t nheader; - nghttp3_nv *nva = NULL; - int rc = 0; - unsigned int i; - nghttp3_data_reader reader; - nghttp3_data_reader *preader = NULL; - CURLcode result; - - *pnwritten = 0; - Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; - result = h3_data_setup(cf, data); - if(result) - goto out; - stream = H3_STREAM_CTX(ctx, data); - DEBUGASSERT(stream); - if(!stream) { - result = CURLE_FAILED_INIT; - goto out; + if(cf->shutdown || !ctx->qconn) { + *done = TRUE; + return CURLE_OK; } - result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, - !data->state.http_ignorecustom ? - data->set.str[STRING_CUSTOMREQUEST] : NULL, - 0, pnwritten); - if(result) - goto out; - if(!stream->h1.done) { - /* need more data */ - goto out; + if(!cf->next) { + Curl_bufq_reset(&ctx->q.sendbuf); + *done = TRUE; + return CURLE_OK; } - DEBUGASSERT(stream->h1.req); - result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); - if(result) - goto out; + CF_DATA_SAVE(save, cf, data); + *done = FALSE; + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); - /* no longer needed */ - Curl_h1_req_parse_free(&stream->h1); + if(!ctx->shutdown_started) { + char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_ssize nwritten; - nheader = Curl_dynhds_count(&h2_headers); - nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); - if(!nva) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", + (int)result); + *done = TRUE; + 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(Curl_bufq_is_empty(&ctx->q.sendbuf)); + ctx->shutdown_started = TRUE; + nwritten = ngtcp2_conn_write_connection_close( + ctx->qconn, NULL, /* path */ + NULL, /* pkt_info */ + (uint8_t *)buffer, sizeof(buffer), + &ctx->last_error, pktx.ts); + CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" + PRIu64 ") -> %zd", (int)ctx->last_error.type, + ctx->last_error.error_code, (ssize_t)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. */ + if(nwritten > 0) { + /* Ignore amount written. sendbuf was empty and has always room for + * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which + * case `result` is set non zero. */ + size_t n; + result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, + (size_t)nwritten, &n); + if(result) { + CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " + "aborting shutdown", (int)result); + goto out; + } - rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); - if(rc) { - failf(data, "can get bidi streams"); - result = CURLE_SEND_ERROR; - goto out; - } - stream->id = sid; - ++ctx->used_bidi_streams; - - 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->h3conn, stream->id, - nva, nheader, preader, data); - if(rc) { - switch(rc) { - case NGHTTP3_ERR_CONN_CLOSING: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " - "connection is closing", stream->id); - break; - default: - CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> " - "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); - break; + ctx->q.no_gso = TRUE; + ctx->q.gsolen = (size_t)nwritten; + ctx->q.split_len = 0; } - 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] [%" PRId64 "] OPENED stream for %s", - stream->id, Curl_bufref_ptr(&data->state.url)); - for(i = 0; i < nheader; ++i) { - infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, - (int)nva[i].namelen, nva[i].name, - (int)nva[i].valuelen, nva[i].value); + if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { + CURL_TRC_CF(data, cf, "shutdown, flushing egress"); + result = vquic_flush(cf, data, &ctx->q); + if(result == CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); + result = CURLE_OK; + goto out; + } + else if(result) { + CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", + (int)result); + *done = TRUE; + goto out; } } + if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { + /* Sent everything off. ngtcp2 seems to have no support for graceful + * shutdowns. We are done. */ + CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); + *done = TRUE; + result = CURLE_OK; + } out: - curlx_free(nva); - Curl_dynhds_free(&h2_headers); + CF_DATA_RESTORE(cf, save); return result; } -static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const uint8_t *buf, size_t len, bool eos, - size_t *pnwritten) +void Curl_cf_ngtcp2_cmn_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - struct cf_call_data save; - struct pkt_io_ctx pktx; - CURLcode result = CURLE_OK; - - CF_DATA_SAVE(save, cf, data); - DEBUGASSERT(cf->connected); - DEBUGASSERT(ctx->qconn); - DEBUGASSERT(ctx->h3conn); - pktx_init(&pktx, cf, data); - *pnwritten = 0; - - /* handshake verification failed in callback, do not send anything */ - if(ctx->tls_vrfy_result) { - result = ctx->tls_vrfy_result; - goto denied; - } + bool done; + Curl_cf_ngtcp2_cmn_shutdown(cf, data, &done); +} - (void)eos; /* use for stream EOF and block handling */ - result = cf_progress_ingress(cf, data, &pktx); - if(result) - goto out; +static bool cf_ngtcp2_err_is_fatal(int code) +{ + return (NGTCP2_ERR_FATAL >= code) || + (NGTCP2_ERR_DROP_CONN == code) || + (NGTCP2_ERR_IDLE_CLOSE == code); +} - if(!stream || stream->id < 0) { - if(ctx->shutdown_started) { - CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); - result = CURLE_SEND_ERROR; - goto out; - } - result = h3_stream_open(cf, data, buf, len, pnwritten); - if(result) { - CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); - goto out; +void Curl_cf_ngtcp2_cmn_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + if(!ctx->last_error.error_code) { + if(NGTCP2_ERR_CRYPTO == code) { + ngtcp2_ccerr_set_tls_alert(&ctx->last_error, + ngtcp2_conn_get_tls_alert(ctx->qconn), + NULL, 0); } - VERBOSE(stream = H3_STREAM_CTX(ctx, data)); - } - else 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) { - if(stream->resp_hds_complete) { - /* Server decided to close the stream after having sent us a final - * response. This is valid if it is not interested in the request - * body. This happens on 30x or 40x responses. - * We silently discard the data sent, since this is not a transport - * error situation. */ - CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" - "on closed stream with response", stream->id); - result = CURLE_OK; - *pnwritten = len; - goto out; + else { + ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0); } - CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " - "-> stream closed", stream->id, len); - result = CURLE_HTTP3; - goto out; - } - else if(ctx->shutdown_started) { - CURL_TRC_CF(data, cf, "cannot send on closed connection"); - result = CURLE_SEND_ERROR; - goto out; - } - else { - result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %d, %zu", - stream->id, len, result, *pnwritten); - if(result) - goto out; - (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); } + if(cf_ngtcp2_err_is_fatal(code)) + Curl_cf_ngtcp2_cmn_conn_close(cf, data); +} - if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) - ctx->earlydata_skip += *pnwritten; +void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + const struct curltime *pnow = Curl_pgrs_now(data); - DEBUGASSERT(!result); - result = cf_progress_egress(cf, data, &pktx); + io_ctx->cf = cf; + io_ctx->data = data; + ngtcp2_path_storage_zero(&io_ctx->ps); + vquic_ctx_set_time(&ctx->q, pnow); + io_ctx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); +} -out: - 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; +void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, + struct cf_ngtcp2_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, pnow); + pktx->ts = ((ngtcp2_tstamp)pnow->tv_sec * NGTCP2_SECONDS) + + ((ngtcp2_tstamp)pnow->tv_usec * NGTCP2_MICROSECONDS); } -struct cf_ngtcp2_recv_ctx { - struct pkt_io_ctx *pktx; - size_t pkt_count; +#if NGTCP2_VERSION_NUM < 0x011100 +struct cf_ngtcp2_sfind_ctx { + int64_t stream_id; + struct h3_stream_ctx *stream; + uint32_t mid; }; -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) +static bool cf_ngtcp2_sfind(uint32_t mid, void *value, void *user_data) { - 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, - ctx->q.local_addrlen); - ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, - remote_addrlen); - pi.ecn = (uint8_t)ecn; - - 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); + struct cf_ngtcp2_sfind_ctx *fctx = user_data; + struct h3_stream_ctx *stream = value; - 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(fctx->stream_id == stream->id) { + fctx->mid = mid; + fctx->stream = stream; + return FALSE; } - return CURLE_OK; + return TRUE; /* continue */ } -static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) +static struct h3_stream_ctx *cf_ngtcp2_get_stream(struct cf_ngtcp2_ctx *ctx, + int64_t stream_id) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct pkt_io_ctx local_pktx; - struct cf_ngtcp2_recv_ctx rctx; - CURLcode result = CURLE_OK; + 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(!pktx) { - pktx_init(&local_pktx, cf, data); - pktx = &local_pktx; + if(!data) { + return NULL; } - result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); - if(result) - return result; - - rctx.pktx = pktx; - rctx.pkt_count = 0; - return vquic_recv_packets(cf, data, &ctx->q, 1000, - cf_ngtcp2_recv_pkts, &rctx); + return H3_STREAM_CTX(ctx, data); } +#endif /** * Read a network packet to send from ngtcp2 into `buf`. @@ -1909,7 +1361,7 @@ static CURLcode read_pkt_to_send(void *userp, unsigned char *buf, size_t buflen, size_t *pnread) { - struct pkt_io_ctx *x = userp; + struct cf_ngtcp2_io_ctx *x = userp; struct cf_ngtcp2_ctx *ctx = x->cf->ctx; nghttp3_vec vec[16]; nghttp3_ssize veccnt; @@ -1939,7 +1391,7 @@ static CURLcode read_pkt_to_send(void *userp, if(veccnt < 0) { failf(x->data, "nghttp3_conn_writev_stream returned error: %s", nghttp3_strerror((int)veccnt)); - cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); + Curl_cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); return CURLE_SEND_ERROR; } } @@ -1983,7 +1435,7 @@ static CURLcode read_pkt_to_send(void *userp, DEBUGASSERT(ndatalen == -1); failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", ngtcp2_strerror((int)n)); - cf_ngtcp2_err_set(x->cf, x->data, (int)n); + Curl_cf_ngtcp2_cmn_err_set(x->cf, x->data, (int)n); return CURLE_SEND_ERROR; } } @@ -2006,9 +1458,9 @@ static CURLcode read_pkt_to_send(void *userp, } } -static CURLcode cf_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) +CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) { struct cf_ngtcp2_ctx *ctx = cf->ctx; size_t nread; @@ -2016,25 +1468,25 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, 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; + CURLcode result; + struct cf_ngtcp2_io_ctx local_pktx; if(!pktx) { - pktx_init(&local_pktx, cf, data); + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); pktx = &local_pktx; } else { - pktx_update_time(data, pktx, cf); + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); ngtcp2_path_storage_zero(&pktx->ps); } - curlcode = vquic_flush(cf, data, &ctx->q); - if(curlcode) { - if(curlcode == CURLE_AGAIN) { + result = vquic_flush(cf, data, &ctx->q); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } /* In UDP, there is a maximum theoretical packet payload length and @@ -2056,12 +1508,12 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, 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 == CURLE_AGAIN) + result = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, + read_pkt_to_send, pktx, &nread); + if(result == CURLE_AGAIN) break; - else if(curlcode) - return curlcode; + else if(result) + return result; else { size_t buflen = Curl_bufq_len(&ctx->q.sendbuf); if((buflen >= send_quantum) || @@ -2079,14 +1531,14 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, /* 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) { + result = vquic_send_tail_split(cf, data, &ctx->q, + gsolen, nread, nread); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } pktcnt = 0; } @@ -2102,797 +1554,334 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, /* 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) { + result = vquic_send(cf, data, &ctx->q, gsolen); + if(result) { + if(result == CURLE_AGAIN) { Curl_expire(data, 1, EXPIRE_QUIC); return CURLE_OK; } - return curlcode; + return result; } - pktx_update_time(data, pktx, cf); + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); ngtcp2_conn_update_pkt_tx_time(ctx->qconn, pktx->ts); } return CURLE_OK; } -static CURLcode h3_data_pause(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool pause) -{ - /* There seems to exist no API in ngtcp2 to shrink/enlarge the streams - * windows. As we do in HTTP/2. */ - (void)cf; - if(!pause) - Curl_multi_mark_dirty(data); - return CURLE_OK; -} +struct cf_ngtcp2_recv_ctx { + struct cf_ngtcp2_io_ctx *pktx; + size_t pkt_count; +}; -static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) +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 cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; + struct cf_ngtcp2_recv_ctx *rctx = userp; + struct cf_ngtcp2_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; - 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->h3conn, stream->id); - } - break; - } - case CF_CTRL_CONN_INFO_UPDATE: - if(!cf->sockindex && cf->connected) { - cf->conn->httpversion_seen = 30; - Curl_conn_set_multiplex(cf->conn); - } - break; - default: - break; + if(!rctx->pkt_count) { + Curl_cf_ngtcp2_io_ctx_update_time(pktx->data, pktx, pktx->cf); + ngtcp2_path_storage_zero(&pktx->ps); } - CF_DATA_RESTORE(cf, save); - return result; -} -static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) -{ - struct cf_call_data save = ctx->call_data; + if(ecn) + CURL_TRC_CF(pktx->data, pktx->cf, "vquic_recv(len=%zu, gso=%zu, ecn=%x)", + buflen, gso_size, (unsigned int)ecn); + ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, + ctx->q.local_addrlen); + ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, + remote_addrlen); + pi.ecn = (uint8_t)ecn; - if(!ctx->initialized) - return; - if(ctx->qlogfd != -1) { - curlx_close(ctx->qlogfd); - } - ctx->qlogfd = -1; - Curl_vquic_tls_cleanup(&ctx->tls); - vquic_ctx_free(&ctx->q); - if(ctx->h3conn) { - nghttp3_conn_del(ctx->h3conn); - ctx->h3conn = NULL; - } - if(ctx->qconn) { - ngtcp2_conn_del(ctx->qconn); - ctx->qconn = NULL; - } -#ifdef OPENSSL_QUIC_API2 - if(ctx->ossl_ctx) { - ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx); - ctx->ossl_ctx = NULL; + 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); + Curl_cf_ngtcp2_cmn_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; + } } -#endif - ctx->call_data = save; + return CURLE_OK; } -static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, bool *done) +CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - struct pkt_io_ctx pktx; + struct cf_ngtcp2_io_ctx local_pktx; + struct cf_ngtcp2_recv_ctx rctx; CURLcode result = CURLE_OK; - if(cf->shutdown || !ctx->qconn) { - *done = TRUE; - return CURLE_OK; + if(!pktx) { + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); + pktx = &local_pktx; } - CF_DATA_SAVE(save, cf, data); - *done = FALSE; - pktx_init(&pktx, cf, data); + result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); + if(result) + return result; - if(!ctx->shutdown_started) { - char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; - ngtcp2_ssize nwritten; + rctx.pktx = pktx; + rctx.pkt_count = 0; - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf"); - result = cf_progress_egress(cf, data, &pktx); - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); - result = CURLE_OK; - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); - *done = TRUE; - goto out; - } + if(ctx->q.sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket (via happy eyeballs) */ + CURL_TRC_CF(data, cf, "progress_ingress(socket)"); + return vquic_recv_packets(cf, data, &ctx->q, 1000, + cf_ngtcp2_recv_pkts, &rctx); + } + else { + /* Tunneled QUIC (CONNECT-UDP through proxy) */ + unsigned char *buf; + size_t max_udp_payload = QUIC_TUNNEL_INBUF_SIZE; + size_t pkt_limit = QUIC_TUNNEL_INGRESS_PKT_LIMIT; + size_t nread; + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen; + + CURL_TRC_CF(data, cf, "progress_ingress(sub-filters)"); + if(ctx->qconn) { + size_t max_path_payload; + max_path_payload = + ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); + if(max_path_payload > max_udp_payload) + max_udp_payload = max_path_payload; } - DEBUGASSERT(Curl_bufq_is_empty(&ctx->q.sendbuf)); - ctx->shutdown_started = TRUE; - nwritten = ngtcp2_conn_write_connection_close( - ctx->qconn, NULL, /* path */ - NULL, /* pkt_info */ - (uint8_t *)buffer, sizeof(buffer), - &ctx->last_error, pktx.ts); - CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - PRIu64 ") -> %zd", ctx->last_error.type, - ctx->last_error.error_code, (ssize_t)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. */ - if(nwritten > 0) { - /* Ignore amount written. sendbuf was empty and has always room for - * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which - * case `result` is set non zero. */ - size_t n; - result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, - (size_t)nwritten, &n); + if(ctx->tunnel_inbuf_len < max_udp_payload) { + unsigned char *newbuf = curlx_realloc(ctx->tunnel_inbuf, + max_udp_payload); + if(!newbuf) + return CURLE_OUT_OF_MEMORY; + ctx->tunnel_inbuf = newbuf; + ctx->tunnel_inbuf_len = max_udp_payload; + } + buf = ctx->tunnel_inbuf; + + while(pkt_limit--) { + result = Curl_conn_cf_recv(cf->next, data, (char *)buf, + ctx->tunnel_inbuf_len, &nread); + if(result == CURLE_AGAIN) { + /* no more data available at the moment */ + return CURLE_OK; + } if(result) { - CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " - "aborting shutdown", result); - goto out; + CURL_TRC_CF(data, cf, "ingress, recv from tunnel failed: %d", + (int)result); + return result; + } + if(nread == 0) { + /* tunnel closed */ + return CURLE_OK; } - ctx->q.no_gso = TRUE; - ctx->q.gsolen = (size_t)nwritten; - ctx->q.split_len = 0; - } - } + memcpy(&remote_addr, ctx->connected_path.remote.addr, + ctx->connected_path.remote.addrlen); + remote_addrlen = (socklen_t)ctx->connected_path.remote.addrlen; + result = cf_ngtcp2_recv_pkts(buf, nread, nread, &remote_addr, + remote_addrlen, 0, &rctx); + if(result) + return result; - if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) { - CURL_TRC_CF(data, cf, "shutdown, flushing egress"); - result = vquic_flush(cf, data, &ctx->q); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "sending shutdown packets blocked"); - result = CURLE_OK; - goto out; - } - else if(result) { - CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result); - *done = TRUE; - goto out; + if(!ctx->q.got_first_byte) { + ctx->q.got_first_byte = TRUE; + ctx->q.first_byte_at = ctx->q.last_op; + } + ctx->q.last_io = ctx->q.last_op; } + return CURLE_OK; } - - if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { - /* Sent everything off. ngtcp2 seems to have no support for graceful - * shutdowns. We are done. */ - CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); - *done = TRUE; - result = CURLE_OK; - } -out: - CF_DATA_RESTORE(cf, save); - return result; -} - -static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - bool done; - cf_ngtcp2_shutdown(cf, data, &done); } -static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) +/** + * Connection maintenance like timeouts on packet ACKs etc. are done by us, not + * the OS like for TCP. POLL events on the socket therefore are not + * sufficient. + * ngtcp2 tells us when it wants to be invoked again. We handle that via + * the `Curl_expire()` mechanisms. + */ +CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - if(ctx && ctx->qconn) { - cf_ngtcp2_conn_close(cf, data); - cf_ngtcp2_ctx_close(ctx); - CURL_TRC_CF(data, cf, "close"); - } - cf->connected = FALSE; - CF_DATA_RESTORE(cf, save); -} + struct cf_ngtcp2_io_ctx local_pktx; + ngtcp2_tstamp expiry; -static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - CURL_TRC_CF(data, cf, "destroy"); - if(cf->ctx) { - cf_ngtcp2_close(cf, data); - cf_ngtcp2_ctx_free(cf->ctx); - cf->ctx = NULL; - } -} - -#ifdef USE_OPENSSL -/* The "new session" callback must return zero if the session can be removed - * or non-zero if the session has been put into the session cache. - */ -static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) -{ - struct Curl_cfilter *cf; - struct cf_ngtcp2_ctx *ctx; - struct Curl_easy *data; - ngtcp2_crypto_conn_ref *cref; - - cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl); - cf = cref ? cref->user_data : NULL; - ctx = cf ? cf->ctx : NULL; - data = cf ? CF_DATA_CURRENT(cf) : NULL; - if(cf && data && ctx) { - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; -#ifdef HAVE_OPENSSL_EARLYDATA - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } -#endif - Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid, - SSL_version(ssl), "h3", quic_tp, quic_tp_len); - } - return 0; -} -#endif /* USE_OPENSSL */ - -#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"; - } - return "Unknown"; -} -#endif - -static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype, - unsigned when, unsigned int incoming, - const gnutls_datum_t *msg) -{ - ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session); - struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL; - - (void)msg; - (void)incoming; - if(when && cf && ctx) { /* after message has been processed */ - struct Curl_easy *data = CF_DATA_CURRENT(cf); - DEBUGASSERT(data); - if(!data) - return 0; - CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]", - incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); - switch(htype) { - case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } - (void)Curl_gtls_cache_session(cf, data, ctx->peer.scache_key, - session, 0, "h3", quic_tp, quic_tp_len); - break; - } - default: - break; - } - } - return 0; -} -#endif /* USE_GNUTLS */ - -#ifdef USE_WOLFSSL -static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) -{ - ngtcp2_crypto_conn_ref *conn_ref = wolfSSL_get_app_data(ssl); - struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL; - - DEBUGASSERT(cf != NULL); - if(cf && session) { - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - DEBUGASSERT(data); - if(data && ctx) { - ngtcp2_ssize tplen; - uint8_t tpbuf[256]; - unsigned char *quic_tp = NULL; - size_t quic_tp_len = 0; - - tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf, - sizeof(tpbuf)); - if(tplen < 0) - CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s", - ngtcp2_strerror((int)tplen)); - else { - quic_tp = (unsigned char *)tpbuf; - quic_tp_len = (size_t)tplen; - } - (void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key, - session, wolfSSL_version(ssl), - "h3", quic_tp, quic_tp_len); - } - } - return 0; -} -#endif /* USE_WOLFSSL */ - -static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, - struct Curl_easy *data, - void *user_data) -{ - struct curl_tls_ctx *ctx = user_data; - -#ifdef USE_OPENSSL -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) - if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) - != 0) { - failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed"); - return CURLE_FAILED_INIT; - } -#elif defined(OPENSSL_QUIC_API2) - /* nothing to do */ -#else - if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_quictls_configure_client_context failed"); - return CURLE_FAILED_INIT; - } -#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ - 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. - */ - SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx, - SSL_SESS_CACHE_CLIENT | - SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb); - } - -#elif defined(USE_GNUTLS) - if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) { - failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed"); - return CURLE_FAILED_INIT; - } - 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); - } - -#elif defined(USE_WOLFSSL) - if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) { - failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); - return CURLE_FAILED_INIT; - } - 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); - } -#endif - return CURLE_OK; -} - -static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct alpn_spec *alpns, - struct Curl_ssl_session *scs, - bool *do_early_data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - - *do_early_data = FALSE; -#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA) - ctx->earlydata_max = scs->earlydata_max; -#endif -#ifdef USE_GNUTLS - ctx->earlydata_max = - gnutls_record_get_max_early_data_size(ctx->tls.gtls.session); -#endif -#ifdef USE_WOLFSSL -#ifdef WOLFSSL_EARLY_DATA - ctx->earlydata_max = scs->earlydata_max; -#else - ctx->earlydata_max = 0; -#endif /* WOLFSSL_EARLY_DATA */ -#endif -#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - (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)) { - CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data"); - } - else if(!scs->quic_tp || !scs->quic_tp_len) { - CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data, "); - } - else { - int rv; - rv = ngtcp2_conn_decode_and_set_0rtt_transport_params( - ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len); - if(rv) - CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport " - "parameters: %s", ngtcp2_strerror(rv)); - else { - infof(data, "SSL session allows %zu bytes of early data, " - "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn); - result = init_ngh3_conn(cf, data); - if(!result) { - ctx->use_earlydata = TRUE; - cf->connected = TRUE; - *do_early_data = TRUE; - } - } - } -#else /* not supported in the TLS backend */ - (void)data; - (void)ctx; - (void)scs; - (void)alpns; -#endif - 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. - */ -static CURLcode cf_connect_start(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct pkt_io_ctx *pktx) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - int rc; - int rv; - CURLcode result; - const struct Curl_sockaddr_ex *sockaddr = NULL; - int qfd; - static const struct alpn_spec ALPN_SPEC_H3 = { { "h3", "h3-29" }, 2 }; - - DEBUGASSERT(ctx->initialized); - ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - ctx->scid.datalen = NGTCP2_MAX_CIDLEN; - result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); - if(result) - return result; - - (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); - ctx->qlogfd = qfd; /* -1 if failure above */ - quic_settings(ctx, data, pktx); - - result = vquic_ctx_init(data, &ctx->q); - if(result) - return result; - - 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); - if(rv == -1) - return CURLE_QUIC_CONNECT_ERROR; - - ngtcp2_addr_init(&ctx->connected_path.local, - (struct sockaddr *)&ctx->q.local_addr, - ctx->q.local_addrlen); - ngtcp2_addr_init(&ctx->connected_path.remote, - &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen); - - rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, - &ctx->connected_path, - NGTCP2_PROTO_VER_V1, &ng_callbacks, - &ctx->settings, &ctx->transport_params, - Curl_ngtcp2_mem(), cf); - if(rc) - return CURLE_QUIC_CONNECT_ERROR; - - ctx->conn_ref.get_conn = get_conn; - ctx->conn_ref.user_data = cf; - - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3, - cf_ngtcp2_tls_ctx_setup, &ctx->tls, - &ctx->conn_ref, - cf_ngtcp2_on_session_reuse); - if(result) - return result; - -#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2) - if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) { - failf(data, "ngtcp2_crypto_ossl_ctx_new failed"); - return CURLE_FAILED_INIT; + if(!pktx) { + Curl_cf_ngtcp2_io_ctx_init(&local_pktx, cf, data); + pktx = &local_pktx; } - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx); - if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) { - failf(data, "ngtcp2_crypto_ossl_configure_client_session failed"); - return CURLE_FAILED_INIT; + else { + Curl_cf_ngtcp2_io_ctx_update_time(data, pktx, cf); } -#elif defined(USE_OPENSSL) - SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0); - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl); -#elif defined(USE_GNUTLS) - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session); -#elif defined(USE_WOLFSSL) - ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl); -#else -#error "ngtcp2 TLS backend not defined" -#endif - ngtcp2_ccerr_default(&ctx->last_error); + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + if(expiry != UINT64_MAX) { + if(expiry <= pktx->ts) { + CURLcode result; + int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); + if(rv) { + failf(data, "ngtcp2_conn_handle_expiry returned error: %s", + ngtcp2_strerror(rv)); + Curl_cf_ngtcp2_cmn_err_set(cf, data, rv); + return CURLE_SEND_ERROR; + } + result = Curl_cf_ngtcp2_progress_ingress(cf, data, pktx); + if(result) + return result; + result = Curl_cf_ngtcp2_progress_egress(cf, data, pktx); + if(result) + return result; + /* ask again, things might have changed */ + expiry = ngtcp2_conn_get_expiry(ctx->qconn); + } + if(expiry > pktx->ts) { + ngtcp2_duration timeout = expiry - pktx->ts; + if(timeout % NGTCP2_MILLISECONDS) { + timeout += NGTCP2_MILLISECONDS; + } + Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS), + EXPIRE_QUIC); + } + } return CURLE_OK; } -static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - struct cf_call_data save; - struct pkt_io_ctx pktx; + const ngtcp2_transport_params *rp; + /* Peer should have sent us its transport parameters. If it + * 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; - if(cf->connected) { - *done = TRUE; - return CURLE_OK; + rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); + if(!rp || !rp->max_idle_timeout) { + ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX); + CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive"); } - - /* Connect the UDP filter first */ - if(!cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; + 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"); } - - *done = FALSE; - - 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; + else { + 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 %" PRIu64 "ms, " + "set keep-alive to %" PRIu64 " ms.", + (rp->max_idle_timeout / NGTCP2_MILLISECONDS), + (keep_ns / NGTCP2_MILLISECONDS)); } +} - pktx_init(&pktx, cf, data); - CF_DATA_SAVE(save, cf, data); +CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - if(!ctx->qconn) { - ctx->started_at = *Curl_pgrs_now(data); - result = cf_connect_start(cf, data, &pktx); - if(result) - goto out; - if(cf->connected) { - *done = TRUE; - goto out; - } - result = cf_progress_egress(cf, data, &pktx); - /* we do not expect to be able to recv anything yet */ - goto out; - } + if(!data) + return CURLE_FAILED_INIT; - result = cf_progress_ingress(cf, data, &pktx); - if(result) - goto out; + if(stream) + return CURLE_OK; - result = cf_progress_egress(cf, data, &pktx); - if(result) - goto out; + stream = curlx_calloc(1, sizeof(*stream)); + if(!stream) + return CURLE_OUT_OF_MEMORY; - if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { - result = ctx->tls_vrfy_result; - if(!result) { - CURL_TRC_CF(data, cf, "peer verified"); - cf->connected = TRUE; - *done = TRUE; - } - } + stream->id = -1; + stream->rx_offset = 0; + stream->rx_offset_max = H3_STREAM_WINDOW_SIZE_INITIAL; -out: - if(ctx->qconn && - ((result == CURLE_RECV_ERROR) || (result == CURLE_SEND_ERROR)) && - ngtcp2_conn_in_draining_period(ctx->qconn)) { - const ngtcp2_ccerr *cerr = ngtcp2_conn_get_ccerr(ctx->qconn); + /* 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); - 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; - } - } - } + if(!Curl_uint32_hash_set(&ctx->streams, data->mid, stream)) { + Curl_cf_ngtcp2_h3_stream_ctx_free(stream); + return CURLE_OUT_OF_MEMORY; } -#ifdef CURLVERBOSE - if(result) { - struct ip_quadruple ip; + if(Curl_uint32_hash_count(&ctx->streams) == 1) + cf_ngtcp2_setup_keep_alive(cf, data); - 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) { - result = check_and_set_expiry(cf, data, &pktx); - } - if(result || *done) - CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); - CF_DATA_RESTORE(cf, save); - return result; + return CURLE_OK; } -static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query, int *pres1, void *pres2) +void Curl_cf_ngtcp2_h3_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct cf_call_data save; + DEBUGASSERT(data); + DEBUGASSERT(stream); + if(!stream->closed && ctx->qconn && ctx->h3conn) { + CURLcode result; - switch(query) { - case CF_QUERY_MAX_CONCURRENT: { - DEBUGASSERT(pres1); - CF_DATA_SAVE(save, cf, data); - /* Set after transport params arrived and continually updated - * by callback. QUIC counts the number over the lifetime of the - * connection, ever increasing. - * We count the *open* transfers plus the budget for new ones. */ - if(!ctx->qconn || ctx->shutdown_started) { - *pres1 = 0; - } - else if(ctx->max_bidi_streams) { - uint64_t avail_bidi_streams = 0; - 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; - *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams; - } - else /* transport params not arrived yet? take our default. */ - *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, 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_ptimediff_ms(&ctx->q.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->q.got_first_byte) - *when = ctx->q.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; + nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL); + ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL); + stream->closed = TRUE; + (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id, + NGHTTP3_H3_REQUEST_CANCELLED); + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); + if(result) + CURL_TRC_CF(data, cf, "[%" PRId64 "] cancel stream -> %d", + stream->id, (int)result); } - default: - break; +} + +void Curl_cf_ngtcp2_h3_stream_done(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)cf; + if(stream) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] easy handle is done", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + Curl_uint32_hash_remove(&ctx->streams, data->mid); + if(!Curl_uint32_hash_count(&ctx->streams)) + cf_ngtcp2_setup_keep_alive(cf, data); } - return cf->next ? - cf->next->cft->query(cf->next, data, query, pres1, pres2) : - CURLE_UNKNOWN_OPTION; } -static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *input_pending) +bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending) { struct cf_ngtcp2_ctx *ctx = cf->ctx; bool alive = FALSE; @@ -2928,8 +1917,8 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, 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, NULL); - CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); + result = Curl_cf_ngtcp2_progress_ingress(cf, data, NULL); + CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", (int)result); alive = result ? FALSE : TRUE; } @@ -2938,60 +1927,43 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, return alive; } -struct Curl_cftype Curl_cft_http3 = { - "HTTP/3", - CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, - 0, - cf_ngtcp2_destroy, - cf_ngtcp2_connect, - cf_ngtcp2_close, - cf_ngtcp2_shutdown, - cf_ngtcp2_adjust_pollset, - Curl_cf_def_data_pending, - cf_ngtcp2_send, - cf_ngtcp2_recv, - cf_ngtcp2_cntrl, - cf_ngtcp2_conn_is_alive, - Curl_cf_def_conn_keep_alive, - cf_ngtcp2_query, -}; - -CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - struct Curl_sockaddr_ex *addr) +CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data) { - struct cf_ngtcp2_ctx *ctx = NULL; - struct Curl_cfilter *cf = NULL; - CURLcode result; + int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; + int rc; - ctx = curlx_calloc(1, sizeof(*ctx)); - if(!ctx) { - result = CURLE_OUT_OF_MEMORY; - goto out; + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } - cf_ngtcp2_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); - if(result) - goto out; - cf->conn = conn; - - result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); - if(result) - goto out; - cf->next->conn = cf->conn; - cf->next->sockindex = cf->sockindex; - -out: - *pcf = (!result) ? cf : NULL; - if(result) { - if(cf) - Curl_conn_cf_discard_chain(&cf, data); - else if(ctx) - cf_ngtcp2_ctx_free(ctx); + rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); + if(rc) { + failf(data, "error binding HTTP/3 control stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; } - return result; + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 qpack encoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); + if(rc) { + failf(data, "error creating HTTP/3 qpack decoding stream: %s", + ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, + qpack_dec_stream_id); + if(rc) { + failf(data, "error binding HTTP/3 qpack streams: %s", ngtcp2_strerror(rc)); + return CURLE_QUIC_CONNECT_ERROR; + } + return CURLE_OK; } -#endif +#endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */ diff --git a/lib/vquic/cf-ngtcp2-cmn.h b/lib/vquic/cf-ngtcp2-cmn.h new file mode 100644 index 000000000000..88554edfb6d3 --- /dev/null +++ b/lib/vquic/cf-ngtcp2-cmn.h @@ -0,0 +1,239 @@ +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_CMN_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" + +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3) + +#include +#include + +#ifdef USE_OPENSSL +#include +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) +#include +#elif defined(OPENSSL_QUIC_API2) +#include +#else +#include +#endif +#include "vtls/openssl.h" +#elif defined(USE_GNUTLS) +#include +#include "vtls/gtls.h" +#elif defined(USE_WOLFSSL) +#include +#include +#include +#include +#include "vtls/wolfssl.h" +#endif + +#ifdef HAVE_NETINET_UDP_H +#include +#endif + +#include + +#include "http1.h" +#include "uint-hash.h" +#include "vtls/vtls.h" +#include "vquic/vquic_int.h" +#include "vquic/vquic-tls.h" + +struct Curl_cfilter; +struct Curl_easy; +struct cf_ngtcp2_ctx; +struct cf_quic_ctx; + +#define QUIC_MAX_STREAMS (256 * 1024) +#define QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS) +#define QUIC_TUNNEL_INBUF_SIZE (64 * 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 +/* 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 buffers do not keep + * spares. Memory consumption goes down when streams run empty, + * have a large upload done, etc. */ +#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_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE) +#define QUIC_TUNNEL_INGRESS_PKT_LIMIT 1000 + + +void Curl_ngtcp2_ver(char *p, size_t len); + +typedef CURLcode cf_ngtcp2_init_h3_conn(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx); + +struct cf_ngtcp2_ctx { + struct cf_quic_ctx q; + struct ssl_peer ssl_peer; + struct curl_tls_ctx tls; +#ifdef OPENSSL_QUIC_API2 + ngtcp2_crypto_ossl_ctx *ossl_ctx; +#endif + ngtcp2_path connected_path; + ngtcp2_conn *qconn; + ngtcp2_cid dcid; + ngtcp2_cid scid; + uint32_t version; + ngtcp2_settings settings; + ngtcp2_transport_params transport_params; + ngtcp2_ccerr last_error; + ngtcp2_crypto_conn_ref conn_ref; + struct cf_call_data call_data; + cf_ngtcp2_init_h3_conn *init_h3_conn_cb; + nghttp3_conn *h3conn; + nghttp3_settings h3settings; + 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 dynbuf scratch; /* temp buffer for header construction */ + struct uint_hash streams; /* hash data->mid to h3_stream_ctx */ + uint64_t used_bidi_streams; /* bidi streams we have opened */ + uint64_t max_bidi_streams; /* max bidi streams we can open */ + size_t earlydata_max; /* max amount of early data supported by + server on session reuse */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + is accepted by peer */ + CURLcode tls_vrfy_result; /* result of TLS peer verification */ + int qlogfd; + unsigned char *tunnel_inbuf; /* ingress buffer for tunneled packets */ + size_t tunnel_inbuf_len; + BIT(initialized); + BIT(tls_handshake_complete); /* TLS handshake is done */ + BIT(use_earlydata); /* Using 0RTT data */ + BIT(earlydata_accepted); /* 0RTT was accepted by server */ + BIT(shutdown_started); /* queued shutdown packets */ +}; + +/* 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 + +CURLcode Curl_cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + cf_ngtcp2_init_h3_conn *init_h3_conn_cb); +void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx); +void Curl_cf_ngtcp2_cmn_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code); + +/** + * All about the H3 internals of a stream + */ +struct h3_stream_ctx { + int64_t id; /* HTTP/3 stream 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" */ + uint64_t error3; /* HTTP/3 stream error code */ + curl_off_t upload_left; /* number of request bytes left to upload */ + curl_off_t rx_total; /* total number of bytes received */ + 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 */ +}; + +void Curl_cf_ngtcp2_h3_stream_ctx_free(struct h3_stream_ctx *stream); +void Curl_cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf, + struct Curl_easy *data, int code); + +CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx, + struct Curl_easy *data); + +CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done); + +CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done); +void Curl_cf_ngtcp2_cmn_conn_close(struct Curl_cfilter *cf, + struct Curl_easy *data); + +struct cf_ngtcp2_io_ctx { + struct Curl_cfilter *cf; + struct Curl_easy *data; + ngtcp2_tstamp ts; + ngtcp2_path_storage ps; +}; + +void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data); +void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx, + struct Curl_cfilter *cf); + +CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_io_ctx *pktx); + +CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf, + struct Curl_easy *data); +void Curl_cf_ngtcp2_h3_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream); +void Curl_cf_ngtcp2_h3_stream_done(struct Curl_cfilter *cf, + struct Curl_easy *data); + +bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *input_pending); + +#endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */ + +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H */ diff --git a/lib/vquic/cf-ngtcp2-proxy.c b/lib/vquic/cf-ngtcp2-proxy.c new file mode 100644 index 000000000000..fd2840890f86 --- /dev/null +++ b/lib/vquic/cf-ngtcp2-proxy.c @@ -0,0 +1,1316 @@ +/*************************************************************************** + * _ _ ____ _ + * 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(CURL_DISABLE_PROXY) && \ + defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + +#include "urldata.h" +#include "url.h" +#include "curl_trc.h" +#include "sendf.h" +#include "multiif.h" +#include "cfilters.h" +#include "connect.h" +#include "progress.h" +#include "curlx/dynbuf.h" +#include "http_proxy.h" +#include "vquic/vquic.h" +#include "vquic/cf-ngtcp2-cmn.h" +#include "vquic/cf-ngtcp2-proxy.h" +#include "capsule.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 PROXY_H3_STREAM_RECV_CHUNKS ((512 * 1024) / H3_STREAM_CHUNK_SIZE) + +typedef enum { + H3_TUNNEL_INIT, /* init/default/no tunnel state */ + H3_TUNNEL_CONNECT, /* CONNECT request is being sent */ + H3_TUNNEL_RESPONSE, /* CONNECT response received completely */ + H3_TUNNEL_ESTABLISHED, + H3_TUNNEL_FAILED +} h3_tunnel_state; + +struct h3_tunnel_stream { + struct Curl_peer *peer; /* where the tunnel goes to */ + struct http_resp *resp; + struct bufq recvbuf; + char *authority; + struct h3_stream_ctx *stream; + h3_tunnel_state state; + BIT(udp); + BIT(has_final_response); + BIT(closed); +}; + +static CURLcode h3_tunnel_stream_init(struct h3_tunnel_stream *ts, + struct Curl_peer *peer, + bool udp) +{ + ts->state = H3_TUNNEL_INIT; + Curl_peer_link(&ts->peer, peer); + Curl_bufq_init2(&ts->recvbuf, H3_STREAM_CHUNK_SIZE, + PROXY_H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); + ts->udp = udp; + /* host:port with IPv6 support */ + ts->authority = curl_maprintf("%s%s%s:%u", peer->ipv6 ? "[" : "", + peer->hostname, + peer->ipv6 ? "]" : "", + peer->port); + if(!ts->authority) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + +static void h3_tunnel_stream_reset(struct h3_tunnel_stream *ts) +{ + Curl_bufq_reset(&ts->recvbuf); + Curl_http_resp_free(ts->resp); + ts->resp = NULL; + ts->stream = NULL; + ts->has_final_response = FALSE; + ts->closed = FALSE; + ts->state = H3_TUNNEL_INIT; +} + +static void h3_tunnel_stream_cleanup(struct h3_tunnel_stream *ts) +{ + Curl_peer_unlink(&ts->peer); + Curl_bufq_free(&ts->recvbuf); + Curl_http_resp_free(ts->resp); + curlx_safefree(ts->authority); + ts->state = H3_TUNNEL_INIT; +} + +static void h3_tunnel_go_state(struct Curl_cfilter *cf, + struct h3_tunnel_stream *ts, + h3_tunnel_state new_state, + struct Curl_easy *data) +{ + VERBOSE(int64_t stream_id = ts->stream ? ts->stream->id : -1); + (void)cf; + + if(ts->state == new_state) + return; + + /* leaving this one */ + switch(ts->state) { + case H3_TUNNEL_CONNECT: + data->req.ignorebody = FALSE; + break; + default: + break; + } + + /* entering this one */ + switch(new_state) { + case H3_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [init]", stream_id); + h3_tunnel_stream_reset(ts); + break; + case H3_TUNNEL_CONNECT: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [connect]", stream_id); + ts->state = H3_TUNNEL_CONNECT; + break; + case H3_TUNNEL_RESPONSE: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [response]", stream_id); + ts->state = H3_TUNNEL_RESPONSE; + break; + case H3_TUNNEL_ESTABLISHED: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [established]", stream_id); + infof(data, "CONNECT%s phase completed for HTTP/3 proxy", + ts->udp ? "-UDP" : ""); + data->state.authproxy.done = TRUE; + data->state.authproxy.multipass = FALSE; + ts->state = new_state; + curlx_safefree(data->req.hd_proxy_auth); + break; + case H3_TUNNEL_FAILED: + CURL_TRC_CF(data, cf, "[%" PRId64 "] -> [failed]", stream_id); + 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. */ + curlx_safefree(data->req.hd_proxy_auth); + break; + } +} + +struct cf_h3_proxy_ctx { + struct cf_ngtcp2_ctx ngtcp2_ctx; + struct h3_tunnel_stream tunnel; /* our tunnel CONNECT stream */ + BIT(connected); +}; + +static CURLcode cf_ngtcp2_proxy_h3_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx); + +static CURLcode cf_h3_proxy_ctx_init(struct cf_h3_proxy_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ + CURLcode result; + result = Curl_cf_ngtcp2_ctx_init(&ctx->ngtcp2_ctx, origin, peer, + sslc, cf_ngtcp2_proxy_h3_init); + if(!result) + result = h3_tunnel_stream_init(&ctx->tunnel, tunnel_peer, + TRNSPRT_IS_DGRAM(tunnel_transport)); + return result; +} + +static void cf_h3_proxy_ctx_free(struct cf_h3_proxy_ctx *ctx) +{ + if(ctx) { + Curl_cf_ngtcp2_ctx_cleanup(&ctx->ngtcp2_ctx); + h3_tunnel_stream_cleanup(&ctx->tunnel); + curlx_free(ctx); + } +} + +static int cb_h3_proxy_acked_req_body(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_h3_proxy_ctx *pctx = cf->ctx; + struct h3_stream_ctx *stream; + size_t skiplen; + (void)stream_user_data; + + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) + 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 int cb_h3_proxy_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_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + + (void)conn; + (void)stream_user_data; + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) + return 0; + + stream->closed = TRUE; + stream->error3 = app_error_code; + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { + stream->reset = TRUE; + stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, + stream->id, stream->error3); + } + else + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + pctx->tunnel.stream = NULL; + pctx->tunnel.closed = TRUE; + Curl_multi_mark_dirty(data); + return 0; +} + +static void cf_h3_proxy_upd_rx_win(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_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(cur_win < wanted_win) { + /* We have exhausted the credit we gave the QUIC peer for DATA. + * We extend it with the amount we can give (rate limit) */ + uint64_t ext = wanted_win - cur_win; + + ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, ext); + ngtcp2_conn_extend_max_offset(ctx->qconn, ext); + stream->rx_offset_max += ext; + if(stream->rx_offset_max > stream->window_size_max) { + stream->window_size_max = stream->rx_offset_max; + CURL_TRC_CF(data, cf, "[%" PRId64 "] max window now -> %" PRIu64, + stream->id, stream->window_size_max); + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] rx_offset_max -> %" PRIu64 + " (ext %" PRIu64 ", win %" PRIu64 ")", + stream->id, stream->rx_offset_max, ext, wanted_win); + } +} + +static int cb_h3_proxy_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_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + size_t nwritten; + CURLcode result = CURLE_OK; + (void)conn; + (void)stream3_id; + (void)stream_user_data; + + stream = pctx->tunnel.stream; + if(!data || !stream || (stream->id != stream3_id)) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + stream->rx_total += (curl_off_t)buflen; + CURL_TRC_CF(data, cf, "[cb_h3_proxy_recv_data] " + "[%" PRId64 "] DATA len=%zu, total=%" FMT_OFF_T, + stream->id, buflen, stream->rx_total); + + result = Curl_bufq_write(&pctx->tunnel.recvbuf, buf, buflen, &nwritten); + if(result || (nwritten < buflen)) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + /* DATA has been moved into our local recv buffer. Update stream offsets + * and give QUIC read credit back so long transfers over proxy tunnels + * do not stall on stream/connection flow-control limits. */ + stream->rx_offset += buflen; + 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=%" PRIu64, + stream->id, buflen, stream->rx_offset_max - stream->rx_offset); + cf_h3_proxy_upd_rx_win(cf, data, stream); + + Curl_multi_mark_dirty(data); + return 0; +} + +static int cb_h3_proxy_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_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + (void)conn; + (void)stream_user_data; + + if(!ctx) + return 0; + + /* 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, stream_id, consumed); + ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); + + return 0; +} + +static int cb_h3_proxy_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_h3_proxy_ctx *pctx = cf->ctx; + nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); + nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + CURLcode result = CURLE_OK; + int http_status; + struct http_resp *resp; + (void)conn; + (void)stream_id; + (void)token; + (void)flags; + (void)stream_user_data; + + /* stream_user_data might be NULL for control streams */ + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] recv_header: stream lookup " + "failed for data=%p mid=%u", + stream_id, (void *)data, data ? data->mid : 0); + return 0; + } + + if(pctx->tunnel.has_final_response) { + /* we do not do anything with trailers for tunnel streams */ + return 0; + } + + if(token == NGHTTP3_QPACK_TOKEN__STATUS) { + result = Curl_http_decode_status(&stream->status_code, + (const char *)h3val.base, h3val.len); + if(result) + return NGHTTP3_ERR_CALLBACK_FAILURE; + http_status = stream->status_code; + result = Curl_http_resp_make(&resp, http_status, NULL); + if(result) + return NGHTTP3_ERR_CALLBACK_FAILURE; + if(pctx->tunnel.resp) + Curl_http_resp_free(pctx->tunnel.resp); + pctx->tunnel.resp = resp; + } + else { + /* store as an HTTP1-style header */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", stream_id, + (int)h3name.len, h3name.base, (int)h3val.len, h3val.base); + result = Curl_dynhds_add(&pctx->tunnel.resp->headers, + (const char *)h3name.base, h3name.len, + (const char *)h3val.base, h3val.len); + if(result) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + return 0; +} + +static int cb_h3_proxy_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_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + (void)conn; + (void)stream_id; + (void)fin; + (void)stream_user_data; + + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + stream = pctx->tunnel.stream; + if(!stream || (stream->id != stream_id)) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers: stream lookup " + "failed for data=%p mid=%u", + stream_id, (void *)data, data ? data->mid : 0); + return 0; + } + + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", stream_id, + stream->status_code); + + if(!pctx->tunnel.has_final_response) { + if(stream->status_code / 100 != 1) { + pctx->tunnel.has_final_response = TRUE; + } + } + + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } + + Curl_multi_mark_dirty(data); + return 0; +} + +static int cb_h3_proxy_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + (void)conn; + (void)stream_user_data; + + if(ctx) { + int rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, + app_error_code); + + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +static int cb_h3_proxy_reset_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + int rv; + + (void)conn; + (void)stream_user_data; + if(!data) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + if(!pctx->tunnel.stream || + (stream_id != pctx->tunnel.stream->id)) + return 0; + + rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, + app_error_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + pctx->tunnel.stream = NULL; + pctx->tunnel.closed = TRUE; + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + return 0; +} + +static nghttp3_ssize cb_h3_tunnel_read_data(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_h3_proxy_ctx *pctx = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + struct h3_stream_ctx *stream; + size_t nwritten = 0; + size_t nvecs = 0; + const unsigned char *buf_base; + + (void)conn; + (void)stream_id; + (void)veccnt; + (void)stream_user_data; + (void)pflags; + + stream = pctx->tunnel.stream; + if(!data || !stream || (stream->id != stream_id)) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + /* nghttp3 keeps references to the sendbuf data until it is ACKed + * by the server (see `cb_h3_proxy_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) { + if(!Curl_bufq_peek_at(&stream->sendbuf, + stream->sendbuf_len_in_flight, + &buf_base, + &vec[nvecs].len)) + break; + vec[nvecs].base = (uint8_t *)(uintptr_t)buf_base; + stream->sendbuf_len_in_flight += vec[nvecs].len; + nwritten += vec[nvecs].len; + ++nvecs; + } + DEBUGASSERT(nvecs > 0); /* we SHOULD have been able to peek */ + } + + if(!nwritten) { + /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", + stream->id); + return NGHTTP3_ERR_WOULDBLOCK; + } + + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + "%zu vecs%s with %zu (buffered=%zu)", + stream->id, nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", + nwritten, Curl_bufq_len(&stream->sendbuf)); + return (nghttp3_ssize)nvecs; +} + +static nghttp3_callbacks ngh3_proxy_callbacks = { + cb_h3_proxy_acked_req_body, /* acked_stream_data */ + cb_h3_proxy_stream_close, + cb_h3_proxy_recv_data, + cb_h3_proxy_deferred_consume, + NULL, /* begin_headers */ + cb_h3_proxy_recv_header, + cb_h3_proxy_end_headers, + NULL, /* begin_trailers */ + cb_h3_proxy_recv_header, + NULL, /* end_trailers */ + cb_h3_proxy_stop_sending, + NULL, /* end_stream */ + cb_h3_proxy_reset_stream, + NULL, /* shutdown */ + NULL, /* recv_settings (deprecated) */ +#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ + NULL, /* recv_origin */ + NULL, /* end_origin */ + NULL, /* rand */ +#endif +#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ + NULL, /* recv_settings2 */ +#endif +}; + +static CURLcode cf_ngtcp2_proxy_h3_init(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx) +{ + int rc; + + if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { + failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3"); + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_settings_default(&ctx->h3settings); + + rc = nghttp3_conn_client_new(&ctx->h3conn, + &ngh3_proxy_callbacks, + &ctx->h3settings, + Curl_nghttp3_mem(), + cf); + if(rc) { + failf(data, "error creating nghttp3 connection instance"); + return CURLE_OUT_OF_MEMORY; + } + + return Curl_cf_ngtcp2_h3_init_ctrls(ctx, data); +} + +static ssize_t cf_h3_proxy_recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + CURLcode *err) +{ + ssize_t nread = -1; + *err = CURLE_OK; + + if(stream->reset) { + 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"); + data->state.refused_stream = TRUE; + *err = CURLE_RECV_ERROR; + goto out; + } + 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)); + nread = 0; + goto out; + } + failf(data, "HTTP/3 stream %" PRId64 " reset by server (error 0x%" PRIx64 + " %s)", stream->id, stream->error3, + vquic_h3_err_str(stream->error3)); + *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; + goto out; + } + else if(!stream->resp_hds_complete) { + failf(data, + "HTTP/3 stream %" PRId64 " was closed cleanly, but before " + "getting all response header fields, treated as error", + stream->id); + *err = CURLE_HTTP3; + goto out; + } + nread = 0; + +out: + return nread; +} + +static CURLcode cf_h3_proxy_sendbuf_add(struct Curl_easy *data, + struct h3_stream_ctx *stream, + const uint8_t *buf, size_t len, + size_t *pnwritten) +{ + CURLcode result; + *pnwritten = 0; + (void)data; + + result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); + return result; +} + +static CURLcode cf_h3_proxy_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *buf, size_t len, + bool eos, size_t *pnwritten) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = NULL; + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + *pnwritten = 0; + + /* handshake verification failed in callback, do not send anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + (void)eos; /* use for stream EOF and block handling */ + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; + + if(pctx->tunnel.closed) { + result = CURLE_SEND_ERROR; + goto denied; + } + + stream = pctx->tunnel.stream; + if(!stream) { + result = CURLE_SEND_ERROR; + goto denied; + } + + if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a final + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + result = CURLE_OK; + *pnwritten = len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + result = CURLE_HTTP3; + goto out; + } + else { + result = cf_h3_proxy_sendbuf_add(data, stream, buf, len, pnwritten); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %d, %zu", + stream->id, len, (int)result, *pnwritten); + if(result) + goto out; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); + } + + if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) + ctx->earlydata_skip += *pnwritten; + + DEBUGASSERT(!result); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, (int)result, *pnwritten); + CF_DATA_RESTORE(cf, save); + return result; +} + +/* incoming data frames on the h3 stream */ +static CURLcode cf_h3_proxy_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = pctx->tunnel.stream; + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + *pnread = 0; + + /* handshake verification failed in callback, do not recv anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + + if(!stream || ctx->shutdown_started) { + result = CURLE_RECV_ERROR; + goto out; + } + + if(!Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) { + result = Curl_bufq_cread(&pctx->tunnel.recvbuf, buf, len, pnread); + if(result) + goto out; + } + + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; + + /* inbufq had nothing before, maybe after progressing ingress? */ + if(!*pnread && !Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) { + result = Curl_bufq_cread(&pctx->tunnel.recvbuf, buf, len, pnread); + if(result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] read inbufq(len=%zu) -> %zu, %d", + stream->id, len, *pnread, (int)result); + goto out; + } + } + + if(*pnread) { + Curl_multi_mark_dirty(data); + } + else { + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + ssize_t nread = + cf_h3_proxy_recv_closed_stream(cf, data, stream, &result); + if(nread > 0) + *pnread = (size_t)nread; + goto out; + } + result = CURLE_AGAIN; + } + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, (int)result, *pnread); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode cf_h3_proxy_submit(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts, + struct httpreq *req) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + struct h3_stream_ctx *stream = NULL; + struct dynhds h2_headers; + nghttp3_nv *nva = NULL; + size_t nheader; + int rc = 0; + unsigned int i; + nghttp3_data_reader reader; + nghttp3_data_reader *preader = NULL; + CURLcode result; + + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + result = Curl_http_req_to_h2(&h2_headers, req, data); + if(result) + goto out; + + result = Curl_cf_ngtcp2_h3_stream_setup(cf, data); + if(result) + goto out; + stream = H3_STREAM_CTX(ctx, data); + DEBUGASSERT(stream); + if(!stream) { + result = CURLE_FAILED_INIT; + goto out; + } + + nheader = Curl_dynhds_count(&h2_headers); + nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + + /* Open a bidirectional stream */ + { + int64_t sid; + int rv; + + DEBUGASSERT(stream->id == -1); + /* Do NOT set `data` as stream user data. The transfer `data` may + * get cleaned up long before the tunnel goes down. */ + rv = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, NULL); + if(rv) { + failf(data, "cannot get bidi streams: %s", ngtcp2_strerror(rv)); + result = CURLE_SEND_ERROR; + goto out; + } + stream->id = sid; + ts->stream = stream; + ++ctx->used_bidi_streams; + CURL_TRC_CF(data, cf, "[%" PRId64 "] opened bidi stream", sid); + } + + /* CONNECT-UDP request stream remains open for capsules, no fixed EOF. */ + stream->send_closed = 0; + reader.read_data = cb_h3_tunnel_read_data; + preader = &reader; + + rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id, + nva, nheader, preader, data); + + if(rc) { + switch(rc) { + case NGHTTP3_ERR_CONN_CLOSING: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " + "connection is closing", stream->id); + break; + default: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> %d (%s)", + stream->id, rc, nghttp3_strerror(rc)); + break; + } + result = CURLE_SEND_ERROR; + goto out; + } + + if(Curl_trc_is_verbose(data)) { + CURL_TRC_CF(data, cf, "[H3-PROXY] [%" PRId64 "] OPENED stream " + "for %s", stream->id, + Curl_bufref_ptr(&data->state.url)); + } + +out: + curlx_free(nva); + Curl_dynhds_free(&h2_headers); + return result; +} + +static CURLcode cf_h3_proxy_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_ngtcp2_ctx *ctx = &pctx->ngtcp2_ctx; + bool want_recv, want_send; + CURLcode result = CURLE_OK; + curl_socket_t sock = (ctx->q.sockfd != CURL_SOCKET_BAD) ? + ctx->q.sockfd : Curl_conn_cf_get_socket(cf, data); + + if(!ctx->qconn || !pctx->tunnel.stream || (sock == CURL_SOCKET_BAD)) + return CURLE_OK; + + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + + if(want_recv || want_send || !Curl_bufq_is_empty(&ctx->q.sendbuf)) { + struct h3_stream_ctx *stream = pctx->tunnel.stream; + bool c_exhaust, s_exhaust; + + c_exhaust = want_send && + (!ngtcp2_conn_get_cwnd_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); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); + } + return result; +} + +static bool cf_h3_proxy_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + if(!Curl_bufq_is_empty(&pctx->tunnel.recvbuf)) + return TRUE; + return cf->next ? + cf->next->cft->has_data_pending(cf->next, data) : FALSE; +} + +static CURLcode cf_h3_proxy_submit_CONNECT(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) +{ + CURLcode result; + struct httpreq *req = NULL; + + result = Curl_http_proxy_create_tunnel_request(&req, cf, data, + ts->peer, + PROXY_HTTP_V3, + (bool)ts->udp); + if(!result) + result = Curl_creader_set_null(data); + if(!result) + result = cf_h3_proxy_submit(cf, data, ts, req); + + if(req) + Curl_http_req_free(req); + if(result) + failf(data, "Failed sending CONNECT to proxy"); + return result; +} + +static CURLcode cf_h3_proxy_inspect_response(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + proxy_inspect_result res; + CURLcode result; + + result = Curl_http_proxy_inspect_tunnel_response( + cf, data, ts->resp, (bool)pctx->tunnel.udp, &res); + if(result) + return result; + switch(res) { + case PROXY_INSPECT_OK: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_ESTABLISHED, data); + break; + case PROXY_INSPECT_FAILED: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); + result = CURLE_COULDNT_CONNECT; + break; + case PROXY_INSPECT_AUTH_RETRY: + h3_tunnel_go_state(cf, ts, H3_TUNNEL_INIT, data); + break; + } + return result; +} + +static CURLcode cf_h3_proxy_tunnel(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_tunnel_stream *ts, + bool *pdone) +{ + struct cf_h3_proxy_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(ts); + DEBUGASSERT(ts->authority); + *pdone = FALSE; + do { + switch(ts->state) { + case H3_TUNNEL_INIT: + CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority); + result = cf_h3_proxy_submit_CONNECT(cf, data, ts); + if(result) + goto out; + h3_tunnel_go_state(cf, ts, H3_TUNNEL_CONNECT, data); + + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); + if(result) + goto out; + FALLTHROUGH(); + + case H3_TUNNEL_CONNECT: + /* Non-blocking: call ingress/egress once and return. + * The multi interface will call us again when ready. */ + result = Curl_cf_ngtcp2_progress_ingress(cf, data, NULL); + if(result) + goto out; + result = Curl_cf_ngtcp2_progress_egress(cf, data, NULL); + if(result && result != CURLE_AGAIN) { + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); + goto out; + } + + if(ts->has_final_response) { + h3_tunnel_go_state(cf, ts, H3_TUNNEL_RESPONSE, data); + } + else { + /* Not done yet, return and let multi interface call us again */ + result = CURLE_OK; + goto out; + } + FALLTHROUGH(); + + case H3_TUNNEL_RESPONSE: + DEBUGASSERT(ts->has_final_response); + result = cf_h3_proxy_inspect_response(cf, data, ts); + if(result) + goto out; + ctx->connected = TRUE; + break; + + case H3_TUNNEL_ESTABLISHED: + *pdone = TRUE; + return CURLE_OK; + + case H3_TUNNEL_FAILED: + return CURLE_RECV_ERROR; + + default: + break; + } + + } while(ts->state == H3_TUNNEL_INIT); + +out: + if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed) + h3_tunnel_go_state(cf, ts, H3_TUNNEL_FAILED, data); + return result; +} + +static CURLcode cf_h3_proxy_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + struct cf_h3_proxy_ctx *pctx = cf->ctx; + struct cf_call_data save = { 0 }; + CURLcode result = CURLE_OK; + struct h3_tunnel_stream *ts = &pctx->tunnel; + bool data_saved = FALSE; + + result = Curl_cf_ngtcp2_cmn_connect(cf, data, done); + if(result || !*done) + goto out; + + CF_DATA_SAVE(save, cf, data); + data_saved = TRUE; + + /* At this point the QUIC is connected, but the proxy is not connected */ + result = cf_h3_proxy_tunnel(cf, data, ts, done); + +out: + if(*done) { + cf->connected = TRUE; + /* The real request will follow the CONNECT, reset request partially */ + Curl_req_soft_reset(&data->req, data); + Curl_client_reset(data); + } + + if(data_saved) + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_h3_proxy_destroy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_h3_proxy_ctx *ctx = cf->ctx; + + (void)data; + if(ctx) { + CURL_TRC_CF(data, cf, "cf_h3_proxy_destroy()"); + cf_h3_proxy_ctx_free(ctx); + cf->ctx = NULL; + } +} + +static CURLcode cf_h3_proxy_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) +{ + return Curl_cf_ngtcp2_cmn_shutdown(cf, data, done); +} + +struct Curl_cftype Curl_cft_h3_proxy = { + "H3-PROXY", + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SSL, + CURL_LOG_LVL_NONE, + cf_h3_proxy_destroy, + cf_h3_proxy_connect, + cf_h3_proxy_shutdown, + cf_h3_proxy_adjust_pollset, + cf_h3_proxy_data_pending, + cf_h3_proxy_send, + cf_h3_proxy_recv, + Curl_cf_def_cntrl, + Curl_cf_ngtcp2_cmn_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ + struct Curl_cfilter *cf = NULL; + struct cf_h3_proxy_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + + if(!tunnel_peer) + return CURLE_FAILED_INIT; + if((transport_peer != TRNSPRT_QUIC) || (!conn->http_proxy.peer)) + return CURLE_FAILED_INIT; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = cf_h3_proxy_ctx_init(ctx, origin, peer, &conn->proxy_ssl_config, + tunnel_peer, tunnel_transport); + if(result) + goto out; + + result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx); + if(result) + goto out; + cf->conn = conn; + + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); + if(result) + goto out; + cf->next->conn = cf->conn; + cf->next->sockindex = cf->sockindex; + +out: + *pcf = (!result) ? cf : NULL; + if(result) { + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) + cf_h3_proxy_ctx_free(ctx); + } + else + CURL_TRC_CF(data, cf, "created, udp_tunnel=%d", ctx->tunnel.udp); + return result; +} + +CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ + struct Curl_cfilter *cf = NULL; + struct cf_h3_proxy_ctx *ctx; + CURLcode result = CURLE_OUT_OF_MEMORY; + (void)data; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) + goto out; + result = cf_h3_proxy_ctx_init(ctx, origin, peer, + &cf_at->conn->proxy_ssl_config, + tunnel_peer, tunnel_transport); + if(result) + goto out; + + result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx); + if(result) + goto out; + + /* H3-PROXY uses the UDP socket created by happy eyeballs below it. + Curl_conn_cf_insert_after chains the existing sub-filters, i.e. + "HAPPY-EYEBALLS -> UDP" as cf->next of H3-PROXY. */ + Curl_conn_cf_insert_after(cf_at, cf); + +out: + if(result) { + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) + cf_h3_proxy_ctx_free(ctx); + } + return result; +} + +#endif diff --git a/lib/vquic/cf-ngtcp2-proxy.h b/lib/vquic/cf-ngtcp2-proxy.h new file mode 100644 index 000000000000..acdee0e46338 --- /dev/null +++ b/lib/vquic/cf-ngtcp2-proxy.h @@ -0,0 +1,52 @@ +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_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" + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \ + defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3) && \ + defined(USE_NGTCP2) && defined(USE_OPENSSL) + +CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); + +CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); + +#endif + +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H */ diff --git a/lib/vquic/cf-ngtcp2.c b/lib/vquic/cf-ngtcp2.c new file mode 100644 index 000000000000..be1a3257c327 --- /dev/null +++ b/lib/vquic/cf-ngtcp2.c @@ -0,0 +1,1166 @@ +/*************************************************************************** + * _ _ ____ _ + * 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_NGTCP2) && defined(USE_NGHTTP3) + +#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/cf-ngtcp2-cmn.h" +#include "vquic/cf-ngtcp2.h" + + +static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, + uint64_t datalen, void *user_data, + void *stream_user_data); + +static CURLcode cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + bool want_recv, want_send; + CURLcode result = CURLE_OK; + curl_socket_t sock = (ctx->q.sockfd != CURL_SOCKET_BAD) ? + ctx->q.sockfd : Curl_conn_cf_get_socket(cf, data); + + if(!ctx->qconn || (sock == CURL_SOCKET_BAD)) + return CURLE_OK; + + Curl_pollset_check(data, ps, sock, &want_recv, &want_send); + if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf)) + want_send = TRUE; + + if(want_recv || want_send) { + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + bool c_exhaust, s_exhaust; + + 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)); + s_exhaust = want_send && stream && stream->id >= 0 && + stream->quic_flow_blocked; + want_recv = (want_recv || c_exhaust || s_exhaust); + want_send = (!s_exhaust && want_send) || + !Curl_bufq_is_empty(&ctx->q.sendbuf); + + result = Curl_pollset_set(data, ps, sock, want_recv, want_send); + CF_DATA_RESTORE(cf, save); + } + return result; +} + +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; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + (void)stream_id; + + /* we might be called by nghttp3 after we already cleaned up */ + if(!stream) + return 0; + + stream->closed = TRUE; + stream->error3 = app_error_code; + if(stream->error3 != NGHTTP3_H3_NO_ERROR) { + stream->reset = TRUE; + stream->send_closed = TRUE; + CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRIu64, + stream->id, stream->error3); + } + else { + CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id); + } + Curl_multi_mark_dirty(data); + return 0; +} + +static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const char *buf, size_t buflen, bool eos) +{ + /* 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, buflen, eos); + if(stream->xfer_result) + CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu " + "bytes of headers", stream->id, (int)stream->xfer_result, + buflen); + } +} + +static void h3_xfer_write_resp(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const char *buf, size_t buflen, bool eos) +{ + /* 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, buflen, eos); + /* If the transfer write is errored, we do not want any more data */ + if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] error %d writing %zu bytes of data", + stream->id, (int)stream->xfer_result, buflen); + } + } +} + +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); + } + } +} + +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_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)stream3_id; + + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + + h3_xfer_write_resp(cf, data, stream, (const char *)buf, buflen, FALSE); + + ngtcp2_conn_extend_max_offset(ctx->qconn, buflen); + stream->rx_offset += buflen; + 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=%" PRIu64, + stream->id, buflen, stream->rx_offset_max - stream->rx_offset); + cf_ngtcp2_upd_rx_win(cf, data, stream); + return 0; +} + +static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, + size_t consumed, 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; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + + /* 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 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; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + (void)conn; + (void)stream_id; + (void)fin; + (void)cf; + + 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"), + (bool)stream->closed); + + CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", + stream_id, stream->status_code); + if(stream->status_code / 100 != 1) { + stream->resp_hds_complete = TRUE; + } + Curl_multi_mark_dirty(data); + return 0; +} + +static int cb_h3_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; + 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) { + + result = Curl_http_decode_status(&stream->status_code, + (const char *)h3val.base, h3val.len); + if(result) + return NGHTTP3_ERR_CALLBACK_FAILURE; + curlx_dyn_reset(&ctx->scratch); + result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 ")); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, + (const char *)h3val.base, h3val.len); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); + 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, "[%" PRId64 "] status: %s", + stream_id, curlx_dyn_ptr(&ctx->scratch)); + if(result) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + else { + /* store as an HTTP1-style header */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", + stream_id, (int)h3name.len, h3name.base, + (int)h3val.len, h3val.base); + curlx_dyn_reset(&ctx->scratch); + result = curlx_dyn_addn(&ctx->scratch, + (const char *)h3name.base, h3name.len); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, STRCONST(": ")); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, + (const char *)h3val.base, h3val.len); + if(!result) + result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n")); + if(!result) + h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), + curlx_dyn_len(&ctx->scratch), FALSE); + } + return 0; +} + +static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + int rv; + (void)conn; + (void)stream_user_data; + + rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, + app_error_code); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) +{ + struct Curl_cfilter *cf = user_data; + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct Curl_easy *data = stream_user_data; + int rv; + (void)conn; + + rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, + app_error_code); + CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); + if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static nghttp3_callbacks ngh3_callbacks = { + cb_h3_acked_req_body, /* acked_stream_data */ + cb_h3_stream_close, + cb_h3_recv_data, + cb_h3_deferred_consume, + NULL, /* begin_headers */ + cb_h3_recv_header, + cb_h3_end_headers, + NULL, /* begin_trailers */ + cb_h3_recv_header, + NULL, /* end_trailers */ + cb_h3_stop_sending, + NULL, /* end_stream */ + cb_h3_reset_stream, + NULL, /* shutdown */ + NULL, /* recv_settings (deprecated) */ +#ifdef NGHTTP3_CALLBACKS_V2 /* nghttp3 v1.11.0+ */ + NULL, /* recv_origin */ + NULL, /* end_origin */ + NULL, /* rand */ +#endif +#ifdef NGHTTP3_CALLBACKS_V3 /* nghttp3 v1.14.0+ */ + NULL, /* recv_settings2 */ +#endif +}; + +static CURLcode init_ngh3_conn(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_ngtcp2_ctx *ctx) +{ + int rc; + + if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { + failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3"); + return CURLE_QUIC_CONNECT_ERROR; + } + + nghttp3_settings_default(&ctx->h3settings); + + rc = nghttp3_conn_client_new(&ctx->h3conn, + &ngh3_callbacks, + &ctx->h3settings, + Curl_nghttp3_mem(), + cf); + if(rc) { + failf(data, "error creating nghttp3 connection instance"); + return CURLE_OUT_OF_MEMORY; + } + + return Curl_cf_ngtcp2_h3_init_ctrls(ctx, data); +} + +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) { + 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 %" PRId64 " was closed cleanly, but before " + "getting all response header fields, treated as error", + stream->id); + return CURLE_HTTP3; + } + return CURLE_OK; +} + +/* incoming data frames on the h3 stream */ +static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t buflen, size_t *pnread) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + int i; + + (void)ctx; + (void)buf; + NOVERBOSE((void)buflen); + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + *pnread = 0; + + /* handshake verification failed in callback, do not recv anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + + if(!stream || ctx->shutdown_started) { + 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); + Curl_cf_ngtcp2_h3_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 && Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx)) { + result = CURLE_RECV_ERROR; + goto out; + } + } + + result = CURLE_AGAIN; + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(buflen=%zu) -> %d, %zu", + stream ? stream->id : -1, buflen, (int)result, *pnread); + CF_DATA_RESTORE(cf, save); + return result; +} + +static int cb_h3_acked_req_body(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_ngtcp2_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_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; + struct Curl_easy *data = stream_user_data; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + size_t nwritten = 0; + size_t nvecs = 0; + (void)cf; + (void)conn; + (void)stream_id; + (void)user_data; + (void)veccnt; + + if(!stream) + return NGHTTP3_ERR_CALLBACK_FAILURE; + /* nghttp3 keeps references to the sendbuf data until it is ACKed + * by the server (see `cb_h3_acked_req_body()` for updates). + * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` + * that we have already passed to nghttp3, but which have not been + * ACKed yet. + * Any amount beyond `sendbuf_len_in_flight` we need still to pass + * to nghttp3. Do that now, if we can. */ + if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { + nvecs = 0; + while(nvecs < veccnt && + Curl_bufq_peek_at(&stream->sendbuf, + stream->sendbuf_len_in_flight, + CURL_UNCONST(&vec[nvecs].base), + &vec[nvecs].len)) { + stream->sendbuf_len_in_flight += vec[nvecs].len; + nwritten += vec[nvecs].len; + ++nvecs; + } + DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ + } + + if(nwritten > 0 && stream->upload_left != -1) + stream->upload_left -= nwritten; + + /* When we stopped sending and everything in `sendbuf` is "in flight", + * we are at the end of the request body. */ + if(stream->upload_left == 0) { + *pflags = NGHTTP3_DATA_FLAG_EOF; + stream->send_closed = TRUE; + } + else if(!nwritten) { + /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", stream->id); + return NGHTTP3_ERR_WOULDBLOCK; + } + + CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " + "%zu vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", + stream->id, nvecs, + *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", + nwritten, Curl_bufq_len(&stream->sendbuf), + stream->upload_left); + return (nghttp3_ssize)nvecs; +} + +static CURLcode h3_stream_open(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *buf, size_t len, + size_t *pnwritten) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = NULL; + int64_t sid; + struct dynhds h2_headers; + size_t nheader; + nghttp3_nv *nva = NULL; + int rc = 0; + unsigned int i; + nghttp3_data_reader reader; + nghttp3_data_reader *preader = NULL; + CURLcode result; + + *pnwritten = 0; + Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); + + result = Curl_cf_ngtcp2_h3_stream_setup(cf, data); + if(result) + goto out; + stream = H3_STREAM_CTX(ctx, data); + DEBUGASSERT(stream); + if(!stream) { + result = CURLE_FAILED_INIT; + goto out; + } + + result = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, + !data->state.http_ignorecustom ? + data->set.str[STRING_CUSTOMREQUEST] : NULL, + 0, pnwritten); + if(result) + goto out; + if(!stream->h1.done) { + /* need more data */ + goto out; + } + DEBUGASSERT(stream->h1.req); + + result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(result) + goto out; + + /* no longer needed */ + Curl_h1_req_parse_free(&stream->h1); + + nheader = Curl_dynhds_count(&h2_headers); + nva = curlx_malloc(sizeof(nghttp3_nv) * nheader); + if(!nva) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + for(i = 0; i < nheader; ++i) { + struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); + nva[i].name = (unsigned char *)e->name; + nva[i].namelen = e->namelen; + nva[i].value = (unsigned char *)e->value; + nva[i].valuelen = e->valuelen; + nva[i].flags = NGHTTP3_NV_FLAG_NONE; + } + + rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); + if(rc) { + failf(data, "cannot open bidi streams"); + result = CURLE_SEND_ERROR; + goto out; + } + stream->id = sid; + ++ctx->used_bidi_streams; + + 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 no 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->h3conn, stream->id, + nva, nheader, preader, data); + if(rc) { + switch(rc) { + case NGHTTP3_ERR_CONN_CLOSING: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send, " + "connection is closing", stream->id); + break; + default: + CURL_TRC_CF(data, cf, "h3sid[%" PRId64 "] failed to send -> " + "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); + break; + } + Curl_cf_ngtcp2_h3_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] [%" PRId64 "] OPENED stream for %s", + stream->id, Curl_bufref_ptr(&data->state.url)); + for(i = 0; i < nheader; ++i) { + infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id, + (int)nva[i].namelen, nva[i].name, + (int)nva[i].valuelen, nva[i].value); + } + } + +out: + curlx_free(nva); + Curl_dynhds_free(&h2_headers); + return result; +} + +static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const uint8_t *buf, size_t len, bool eos, + size_t *pnwritten) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); + struct cf_call_data save; + struct cf_ngtcp2_io_ctx pktx; + CURLcode result = CURLE_OK; + + CF_DATA_SAVE(save, cf, data); + DEBUGASSERT(cf->connected); + DEBUGASSERT(ctx->qconn); + DEBUGASSERT(ctx->h3conn); + Curl_cf_ngtcp2_io_ctx_init(&pktx, cf, data); + *pnwritten = 0; + + /* handshake verification failed in callback, do not send anything */ + if(ctx->tls_vrfy_result) { + result = ctx->tls_vrfy_result; + goto denied; + } + + (void)eos; /* use for stream EOF and block handling */ + result = Curl_cf_ngtcp2_progress_ingress(cf, data, &pktx); + if(result) + goto out; + + if(!stream || stream->id < 0) { + if(ctx->shutdown_started) { + CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); + result = CURLE_SEND_ERROR; + goto out; + } + result = h3_stream_open(cf, data, buf, len, pnwritten); + if(result) { + CURL_TRC_CF(data, cf, "failed to open stream -> %d", (int)result); + goto out; + } + VERBOSE(stream = H3_STREAM_CTX(ctx, data)); + } + else if(stream->xfer_result) { + CURL_TRC_CF(data, cf, "[%" PRId64 "] xfer write failed", stream->id); + Curl_cf_ngtcp2_h3_stream_close(cf, data, stream); + result = stream->xfer_result; + goto out; + } + else if(stream->closed) { + if(stream->resp_hds_complete) { + /* Server decided to close the stream after having sent us a final + * response. This is valid if it is not interested in the request + * body. This happens on 30x or 40x responses. + * We silently discard the data sent, since this is not a transport + * error situation. */ + CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" + "on closed stream with response", stream->id); + result = CURLE_OK; + *pnwritten = len; + goto out; + } + CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " + "-> stream closed", stream->id, len); + result = CURLE_HTTP3; + goto out; + } + else if(ctx->shutdown_started) { + CURL_TRC_CF(data, cf, "cannot send on closed connection"); + result = CURLE_SEND_ERROR; + goto out; + } + else { + result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " + "sendbuf(len=%zu) -> %d, %zu", + stream->id, len, (int)result, *pnwritten); + if(result) + goto out; + (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); + } + + if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) + ctx->earlydata_skip += *pnwritten; + + DEBUGASSERT(!result); + result = Curl_cf_ngtcp2_progress_egress(cf, data, &pktx); + +out: + result = Curl_1st_fatal(result, + Curl_cf_ngtcp2_cmn_set_expiry(cf, data, &pktx)); + if(ctx->tls_vrfy_result) + result = ctx->tls_vrfy_result; +denied: + CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, (int)result, *pnwritten); + CF_DATA_RESTORE(cf, save); + return result; +} + +static CURLcode h3_data_pause(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool pause) +{ + /* There seems to exist no API in ngtcp2 to shrink/enlarge the streams + * windows. As we do in HTTP/2. */ + (void)cf; + if(!pause) + Curl_multi_mark_dirty(data); + return CURLE_OK; +} + +static CURLcode cf_ngtcp2_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_ngtcp2_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: + Curl_cf_ngtcp2_h3_stream_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->h3conn, stream->id); + } + break; + } + case CF_CTRL_CONN_INFO_UPDATE: + if(!cf->sockindex && cf->connected) { + cf->conn->httpversion_seen = 30; + Curl_conn_set_multiplex(cf->conn); + } + break; + default: + break; + } + CF_DATA_RESTORE(cf, save); + return result; +} + +static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx) +{ + struct cf_call_data save = ctx->call_data; + + if(!ctx->initialized) + return; + if(ctx->qlogfd != -1) { + curlx_close(ctx->qlogfd); + } + ctx->qlogfd = -1; + Curl_vquic_tls_cleanup(&ctx->tls); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); + vquic_ctx_free(&ctx->q); + if(ctx->h3conn) { + nghttp3_conn_del(ctx->h3conn); + ctx->h3conn = NULL; + } + if(ctx->qconn) { + ngtcp2_conn_del(ctx->qconn); + ctx->qconn = NULL; + } +#ifdef OPENSSL_QUIC_API2 + if(ctx->ossl_ctx) { + ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx); + ctx->ossl_ctx = NULL; + } +#endif + ctx->call_data = save; +} + +static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "destroy"); + if(ctx) { + if(ctx->qconn) { + struct cf_call_data save; + CF_DATA_SAVE(save, cf, data); + Curl_cf_ngtcp2_cmn_conn_close(cf, data); + cf_ngtcp2_ctx_close(ctx); + CF_DATA_RESTORE(cf, save); + } + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); + cf->ctx = NULL; + } +} + +static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + return Curl_cf_ngtcp2_cmn_connect(cf, data, done); +} + +static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) +{ + struct cf_ngtcp2_ctx *ctx = cf->ctx; + struct cf_call_data save; + + switch(query) { + case CF_QUERY_MAX_CONCURRENT: { + DEBUGASSERT(pres1); + CF_DATA_SAVE(save, cf, data); + /* Set after transport params arrived and continually updated + * by callback. QUIC counts the number over the lifetime of the + * connection, ever increasing. + * We count the *open* transfers plus the budget for new ones. */ + if(!ctx->qconn || ctx->shutdown_started) { + *pres1 = 0; + } + else if(ctx->max_bidi_streams) { + uint64_t avail_bidi_streams = 0; + 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; + *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams; + } + else /* transport params not arrived yet? take our default. */ + *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, 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_ptimediff_ms(&ctx->q.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->q.got_first_byte) + *when = ctx->q.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_ngtcp2_destroy, + cf_ngtcp2_connect, + Curl_cf_ngtcp2_cmn_shutdown, + cf_ngtcp2_adjust_pollset, + Curl_cf_def_data_pending, + cf_ngtcp2_send, + cf_ngtcp2_recv, + cf_ngtcp2_cntrl, + Curl_cf_ngtcp2_cmn_conn_is_alive, + Curl_cf_def_conn_keep_alive, + cf_ngtcp2_query, +}; + +CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr) +{ + struct cf_ngtcp2_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = Curl_cf_ngtcp2_ctx_init(ctx, origin, peer, + &conn->ssl_config, init_ngh3_conn); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + cf->conn = conn; + + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); + if(result) + goto out; + cf->next->conn = cf->conn; + cf->next->sockindex = cf->sockindex; + +out: + *pcf = (!result) ? cf : NULL; + if(result) { + if(cf) + Curl_conn_cf_discard_chain(&cf, data); + else if(ctx) { + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); + } + } + return result; +} + +CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) +{ + struct cf_ngtcp2_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = Curl_cf_ngtcp2_ctx_init(ctx, origin, peer, + &cf_at->conn->ssl_config, init_ngh3_conn); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); +out: + if(result) { + curlx_safefree(cf); + if(ctx) { + Curl_cf_ngtcp2_ctx_cleanup(ctx); + curlx_free(ctx); + } + } + return result; +} + +#endif diff --git a/lib/vquic/curl_ngtcp2.h b/lib/vquic/cf-ngtcp2.h similarity index 81% rename from lib/vquic/curl_ngtcp2.h rename to lib/vquic/cf-ngtcp2.h index 185272ace030..601efc82245a 100644 --- a/lib/vquic/curl_ngtcp2.h +++ b/lib/vquic/cf-ngtcp2.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H -#define HEADER_CURL_VQUIC_CURL_NGTCP2_H +#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_H +#define HEADER_CURL_VQUIC_CF_NGTCP2_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -48,12 +48,16 @@ struct Curl_cfilter; #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 Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr); + +CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); #endif -#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */ +#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_H */ diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/cf-quiche.c similarity index 95% rename from lib/vquic/curl_quiche.c rename to lib/vquic/cf-quiche.c index 73f664a65344..31a3957ec372 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/cf-quiche.c @@ -41,10 +41,11 @@ #include "progress.h" #include "select.h" #include "http1.h" +#include "sockaddr.h" #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/vquic-tls.h" -#include "vquic/curl_quiche.h" +#include "vquic/cf-quiche.h" #include "transfer.h" #include "url.h" #include "bufref.h" @@ -75,7 +76,7 @@ void Curl_quiche_ver(char *p, size_t len) struct cf_quiche_ctx { struct cf_quic_ctx q; - struct ssl_peer peer; + struct ssl_peer ssl_peer; struct curl_tls_ctx tls; quiche_conn *qconn; quiche_config *cfg; @@ -106,7 +107,10 @@ static void quiche_debug_log(const char *line, void *argp) static void h3_stream_hash_free(unsigned int id, void *stream); -static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) +static CURLcode cf_quiche_ctx_init(struct cf_quiche_ctx *ctx, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc) { DEBUGASSERT(!ctx->initialized); #ifdef DEBUG_QUICHE @@ -121,6 +125,7 @@ static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx) BUFQ_OPT_SOFT_LIMIT); ctx->data_recvd = 0; ctx->initialized = TRUE; + return Curl_vquic_tls_peer_init(origin, peer, sslc, &ctx->ssl_peer); } static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) @@ -129,7 +134,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) /* quiche freed it */ ctx->tls.ossl.ssl = NULL; Curl_vquic_tls_cleanup(&ctx->tls); - Curl_ssl_peer_cleanup(&ctx->peer); + Curl_ssl_peer_cleanup(&ctx->ssl_peer); vquic_ctx_free(&ctx->q); Curl_uint32_hash_destroy(&ctx->streams); curlx_dyn_free(&ctx->h1hdr); @@ -156,6 +161,7 @@ static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx) quiche_config_free(ctx->cfg); ctx->cfg = NULL; } + Curl_ssl_peer_cleanup(&ctx->ssl_peer); } static CURLcode cf_flush_egress(struct Curl_cfilter *cf, @@ -298,7 +304,7 @@ static void cf_quiche_stream_close(struct Curl_cfilter *cf, result = cf_flush_egress(cf, data); if(result) CURL_TRC_CF(data, cf, "[%" PRIu64 "] stream close, flush egress -> %d", - stream->id, result); + stream->id, (int)result); } } @@ -338,7 +344,8 @@ static void cf_quiche_write_hd(struct Curl_cfilter *cf, 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); + "bytes of headers", stream->id, (int)stream->xfer_result, + blen); } } @@ -418,7 +425,7 @@ static int cb_each_header(uint8_t *name, size_t name_len, if(result) { CURL_TRC_CF(x->data, x->cf, "[%" PRIu64 "] on header error %d", - stream->id, result); + stream->id, (int)result); if(!stream->xfer_result) stream->xfer_result = result; } @@ -459,7 +466,7 @@ static void cf_quiche_flush_body(struct Curl_cfilter *cf, 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); + " of data", stream->id, (int)stream->xfer_result, blen); } } else @@ -496,9 +503,9 @@ static void cf_quiche_recv_body(struct Curl_cfilter *cf, break; else if(result) { CURL_TRC_CF(data, cf, "[%" PRIu64 "] recv_body error %d", - stream->id, result); + stream->id, (int)result); failf(data, "[%" PRIu64 "] Error %d in HTTP/3 response body for stream", - stream->id, result); + stream->id, (int)result); stream->closed = TRUE; stream->reset = TRUE; stream->send_closed = TRUE; @@ -514,10 +521,14 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf, struct h3_stream_ctx *stream, quiche_h3_event *ev) { + enum quiche_h3_event_type type; + if(!stream) return; - switch(quiche_h3_event_type(ev)) { + type = quiche_h3_event_type(ev); + + switch(type) { case QUICHE_H3_EVENT_HEADERS: { struct cb_ctx cb_ctx; stream->resp_got_header = TRUE; @@ -563,7 +574,7 @@ static void cf_quiche_process_ev(struct Curl_cfilter *cf, default: CURL_TRC_CF(data, cf, "[%" PRIu64 "] recv, unhandled event %d", - stream->id, quiche_h3_event_type(ev)); + stream->id, (int)type); break; } } @@ -871,7 +882,7 @@ static CURLcode recv_closed_stream(struct Curl_cfilter *cf, vquic_h3_err_str(stream->error3)); result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv, was reset -> %d", - stream->id, result); + stream->id, (int)result); } else if(!stream->resp_got_header) { failf(data, "HTTP/3 stream %" PRIu64 " was closed cleanly, but before " @@ -922,7 +933,8 @@ static CURLcode cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(*pnread > 0) ctx->data_recvd += *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); + FMT_OFF_T, stream->id, blen, (int)result, *pnread, + ctx->data_recvd); return result; } @@ -1148,7 +1160,7 @@ static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : (uint64_t)~0, len, - result, *pnwritten); + (int)result, *pnwritten); return result; } @@ -1232,7 +1244,7 @@ static CURLcode cf_quiche_cntrl(struct Curl_cfilter *cf, body[0] = 'X'; result = cf_quiche_send(cf, data, body, 0, TRUE, &sent); CURL_TRC_CF(data, cf, "[%" PRIu64 "] DONE_SEND -> %d, %zu", - stream->id, result, sent); + stream->id, (int)result, sent); } break; } @@ -1290,7 +1302,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1); - result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, + result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->ssl_peer, &ALPN_SPEC_H3, NULL, NULL, cf, NULL); if(result) return result; @@ -1356,7 +1368,7 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); + return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->ssl_peer); } static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, @@ -1380,7 +1392,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, *done = FALSE; if(Curl_ossl_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, ctx->ssl_peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); return CURLE_OK; } @@ -1498,16 +1510,6 @@ static CURLcode cf_quiche_shutdown(struct Curl_cfilter *cf, return result; } -static void cf_quiche_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - if(cf->ctx) { - bool done; - (void)cf_quiche_shutdown(cf, data, &done); - cf_quiche_ctx_close(cf->ctx); - cf->connected = FALSE; - } -} - static void cf_quiche_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { (void)data; @@ -1625,7 +1627,6 @@ struct Curl_cftype Curl_cft_http3 = { 0, cf_quiche_destroy, cf_quiche_connect, - cf_quiche_close, cf_quiche_shutdown, cf_quiche_adjust_pollset, Curl_cf_def_data_pending, @@ -1639,6 +1640,8 @@ struct Curl_cftype Curl_cft_http3 = { CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr) { @@ -1651,14 +1654,15 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, result = CURLE_OUT_OF_MEMORY; goto out; } - cf_quiche_ctx_init(ctx); - - result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + result = cf_quiche_ctx_init(ctx, origin, peer, &conn->ssl_config); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); if(result) goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC, + conn, addr, NULL, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn; @@ -1676,4 +1680,34 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, return result; } +CURLcode Curl_cf_quiche_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) +{ + struct cf_quiche_ctx *ctx = NULL; + struct Curl_cfilter *cf = NULL; + CURLcode result; + + ctx = curlx_calloc(1, sizeof(*ctx)); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + result = cf_quiche_ctx_init(ctx, origin, peer, &cf_at->conn->ssl_config); + if(!result) + result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); + if(result) + goto out; + Curl_conn_cf_insert_after(cf_at, cf); + +out: + if(result) { + curlx_safefree(cf); + if(ctx) + cf_quiche_ctx_free(ctx); + } + + return result; +} + #endif diff --git a/lib/vquic/curl_quiche.h b/lib/vquic/cf-quiche.h similarity index 78% rename from lib/vquic/curl_quiche.h rename to lib/vquic/cf-quiche.h index c2c88ddeafe3..88d9161dd768 100644 --- a/lib/vquic/curl_quiche.h +++ b/lib/vquic/cf-quiche.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H -#define HEADER_CURL_VQUIC_CURL_QUICHE_H +#ifndef HEADER_CURL_VQUIC_CF_QUICHE_H +#define HEADER_CURL_VQUIC_CF_QUICHE_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -37,9 +37,14 @@ void Curl_quiche_ver(char *p, size_t len); CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr); +CURLcode Curl_cf_quiche_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); #endif -#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */ +#endif /* HEADER_CURL_VQUIC_CF_QUICHE_H */ diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index d81f2a9e6bd5..58f139306a3d 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -49,17 +49,12 @@ #include "vtls/vtls_scache.h" #include "vquic/vquic-tls.h" -CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const struct alpn_spec *alpns, - Curl_vquic_tls_ctx_setup *cb_setup, - void *cb_user_data, void *ssl_user_data, - Curl_vquic_session_reuse_cb *session_reuse_cb) +CURLcode Curl_vquic_tls_peer_init(struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct ssl_peer *ssl_peer) { char tls_id[80]; - CURLcode result; #ifdef USE_OPENSSL Curl_ossl_version(tls_id, sizeof(tls_id)); @@ -71,22 +66,31 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, #error "no TLS lib in used, should not happen" return CURLE_FAILED_INIT; #endif - (void)session_reuse_cb; - result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC); - if(result) - return result; + if(ssl_peer->origin || ssl_peer->peer) + Curl_ssl_peer_cleanup(ssl_peer); + return Curl_ssl_peer_init(ssl_peer, origin, peer, sslc, + tls_id, TRNSPRT_QUIC); +} +CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *ssl_peer, + const struct alpn_spec *alpns, + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data, void *ssl_user_data, + Curl_vquic_session_reuse_cb *session_reuse_cb) +{ #ifdef USE_OPENSSL - (void)result; - return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, alpns, + return Curl_ossl_ctx_init(&ctx->ossl, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, NULL, ssl_user_data, session_reuse_cb); #elif defined(USE_GNUTLS) - return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, alpns, + return Curl_gtls_ctx_init(&ctx->gtls, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, ssl_user_data, session_reuse_cb); #elif defined(USE_WOLFSSL) - return Curl_wssl_ctx_init(&ctx->wssl, cf, data, peer, alpns, + return Curl_wssl_ctx_init(&ctx->wssl, cf, data, ssl_peer, alpns, cb_setup, cb_user_data, ssl_user_data, session_reuse_cb); #else @@ -178,7 +182,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, NULL) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; else if(!peer->sni && - (wolfSSL_X509_check_ip_asc(cert, peer->hostname, + (wolfSSL_X509_check_ip_asc(cert, peer->origin->hostname, 0) == WOLFSSL_FAILURE)) result = CURLE_PEER_FAILED_VERIFICATION; wolfSSL_X509_free(cert); diff --git a/lib/vquic/vquic-tls.h b/lib/vquic/vquic-tls.h index c461c1548b17..e8d2418b0611 100644 --- a/lib/vquic/vquic-tls.h +++ b/lib/vquic/vquic-tls.h @@ -66,13 +66,18 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, struct Curl_ssl_session *scs, bool *do_early_data); +CURLcode Curl_vquic_tls_peer_init(struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, + struct ssl_peer *ssl_peer); + /** * 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 to be connected to + * @param ssl_peer the SSL 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 @@ -82,7 +87,7 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf, struct Curl_easy *data, - struct ssl_peer *peer, + struct ssl_peer *ssl_peer, const struct alpn_spec *alpns, Curl_vquic_tls_ctx_setup *cb_setup, void *cb_user_data, diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 0040ad671fb8..dfb8346c1ad3 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -41,8 +41,10 @@ #include "curlx/dynbuf.h" #include "curlx/fopen.h" #include "cfilters.h" -#include "vquic/curl_ngtcp2.h" -#include "vquic/curl_quiche.h" +#include "vquic/cf-ngtcp2.h" +#include "vquic/cf-ngtcp2-cmn.h" +#include "vquic/cf-ngtcp2-proxy.h" +#include "vquic/cf-quiche.h" #include "multiif.h" #include "progress.h" #include "rand.h" @@ -163,12 +165,10 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, ; if(!curlx_sztouz(rv, psent)) { - switch(SOCKERRNO) { - case EAGAIN: -#if EAGAIN != SOCKEWOULDBLOCK - case SOCKEWOULDBLOCK: -#endif + int sockerr = SOCKERRNO; + if(SOCK_EAGAIN(sockerr)) return CURLE_AGAIN; + switch(sockerr) { case SOCKEMSGSIZE: /* UDP datagram is too large; caused by PMTUD. Let it be lost. */ *psent = pktlen; @@ -177,13 +177,13 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, if(pktlen > gsolen) { /* GSO failure */ infof(data, "sendmsg() returned %zd (errno %d); disable GSO", rv, - SOCKERRNO); + sockerr); 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)", rv, SOCKERRNO); + failf(data, "sendmsg() returned %zd (errno %d)", rv, sockerr); result = CURLE_SEND_ERROR; goto out; } @@ -204,7 +204,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, ; if(!curlx_sztouz(rv, psent)) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { result = CURLE_AGAIN; goto out; } @@ -255,9 +255,48 @@ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, VERBOSE(++calls); } 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); + CURL_TRC_CF(data, cf, + "vquic_%s(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu", + VQUIC_SEND_METHOD, pktlen, gsolen, calls, (int)result, *psent); + return result; +} + +/* Split QUIC payload by datagram (gso) boundaries when sending over a + * non-UDP lower filter (for example CONNECT-UDP proxy tunnel). */ +static CURLcode send_packet_no_gso_cf(struct Curl_cfilter *cf, + struct Curl_easy *data, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent) +{ + const uint8_t *p, *end = pkt + pktlen; + size_t sent, len; + CURLcode result = CURLE_OK; + VERBOSE(size_t calls = 0); + + *psent = 0; + + /* Send one datagram-sized chunk per call into the lower filter. */ + for(p = pkt; p < end; p += len) { + len = CURLMIN(gsolen, (size_t)(end - p)); + result = Curl_conn_cf_send(cf->next, data, p, len, FALSE, &sent); + /* Report forward progress even if we return CURLE_AGAIN later. */ + VERBOSE(++calls); + /* Preserve lower-filter errors (including CURLE_AGAIN). */ + if(result) + goto out; + + if(sent != len) { + /* We can only send the complete datagram, not parts. */ + result = CURLE_SEND_ERROR; + goto out; + } + *psent += sent; + } + +out: + CURL_TRC_CF(data, cf, + "vquic_cf_send(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu", + pktlen, gsolen, calls, (int)result, *psent); return result; } @@ -285,9 +324,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); + CURL_TRC_CF(data, cf, + "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu", + VQUIC_SEND_METHOD, pktlen, gsolen, (int)result, *psent); } if(!result) qctx->last_io = qctx->last_op; @@ -310,7 +349,22 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, blen = qctx->split_len; } - result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); + if(qctx->sockfd != CURL_SOCKET_BAD) { + /* Direct UDP socket (via happy eyeballs) */ + result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); + } + else { + /* Tunneled QUIC (CONNECT-UDP through proxy) */ + if(gsolen && (blen > gsolen)) { + /* Send one datagram at a time to preserve packet boundaries. */ + result = send_packet_no_gso_cf(cf, data, buf, blen, gsolen, &sent); + } + else { + /* No GSO aggregate to split, regular lower-filter send is enough. */ + result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &sent); + } + } + if(result) { if(result == CURLE_AGAIN) { Curl_bufq_skip(&qctx->sendbuf, sent); @@ -419,7 +473,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, memset(&mmsg, 0, sizeof(mmsg)); for(i = 0; i < n; ++i) { msg_iov[i].iov_base = bufs[i]; - msg_iov[i].iov_len = (int)sizeof(bufs[i]); + msg_iov[i].iov_len = sizeof(bufs[i]); mmsg[i].msg_hdr.msg_iov = &msg_iov[i]; mmsg[i].msg_hdr.msg_iovlen = 1; mmsg[i].msg_hdr.msg_name = &remote_addr[i]; @@ -432,7 +486,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; if(mcount == -1) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN"); goto out; } @@ -446,7 +500,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, } curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); failf(data, "QUIC: recvmmsg() unexpectedly returned %d (errno=%d; %s)", - mcount, SOCKERRNO, errstr); + mcount, SOCKERRNO, errstr); result = CURLE_RECV_ERROR; goto out; } @@ -454,8 +508,10 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, VERBOSE(++calls); for(i = 0; i < mcount; ++i) { /* A zero-length UDP packet is no QUIC packet. Ignore. */ - if(!mmsg[i].msg_len) + if(!mmsg[i].msg_len) { + ++pkts; continue; + } total_nread += mmsg[i].msg_len; gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr); @@ -473,8 +529,9 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, (int)result); Curl_multi_xfer_sockbuf_release(data, sockbuf); return result; } @@ -500,11 +557,11 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, DEBUGASSERT(max_pkts > 0); for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) { - /* fully initialise this on each call to `recvmsg()`. There seem to + /* fully initialize 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)); msg_iov.iov_base = buf; - msg_iov.iov_len = (int)sizeof(buf); + msg_iov.iov_len = sizeof(buf); msg.msg_iov = &msg_iov; msg.msg_iovlen = 1; msg.msg_control = msg_ctrl; @@ -516,7 +573,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; if(!curlx_sztouz(rc, &nread)) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { goto out; } if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) { @@ -538,8 +595,10 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, ++calls; /* A 0-length UDP packet is no QUIC packet */ - if(!nread) + if(!nread) { + ++pkts; continue; + } gso_size = vquic_msghdr_get_udp_gro(&msg); if(gso_size == 0) @@ -554,8 +613,9 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, (int)result); return result; } @@ -583,7 +643,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) ; if(!curlx_sztouz(rv, &nread)) { - if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN"); goto out; } @@ -618,8 +678,9 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, out: if(total_nread || result) - CURL_TRC_CF(data, cf, "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu)" - " -> %d", total_nread, pkts, calls, result); + CURL_TRC_CF(data, cf, + "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu) -> %d", + total_nread, pkts, calls, (int)result); return result; } #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ @@ -699,27 +760,106 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, return CURLE_OK; } +CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer) +{ +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_insert_after(cf_at, origin, peer); +#elif defined(USE_QUICHE) + return Curl_cf_quiche_insert_after(cf_at, origin, peer); +#else + (void)cf_at; + (void)origin; + (void)peer; + return CURLE_NOT_BUILT_IN; +#endif +} + CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport) + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) { - (void)transport; - DEBUGASSERT(transport == TRNSPRT_QUIC); + (void)transport_peer; + (void)tunnel_transport; + (void)tunnel_peer; + DEBUGASSERT(transport_peer == TRNSPRT_QUIC); #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_create(pcf, data, conn, addr); + return Curl_cf_ngtcp2_create(pcf, data, origin, peer, conn, addr); #elif defined(USE_QUICHE) - return Curl_cf_quiche_create(pcf, data, conn, addr); + return Curl_cf_quiche_create(pcf, data, origin, peer, conn, addr); +#else + *pcf = NULL; + (void)data; + (void)origin; + (void)peer; + (void)conn; + (void)addr; + (void)tunnel_peer; + (void)tunnel_transport; + return CURLE_NOT_BUILT_IN; +#endif +} + +#if !defined(CURL_DISABLE_PROXY) && defined(USE_PROXY_HTTP3) + +CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_proxy_insert_after(cf_at, data, origin, peer, + tunnel_peer, tunnel_transport); +#else + (void)cf_at; + (void)data; + (void)origin; + (void)peer; + (void)tunnel_peer; + (void)tunnel_transport; + return CURLE_NOT_BUILT_IN; +#endif +} + +CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport) +{ + DEBUGASSERT(transport_peer == TRNSPRT_QUIC); +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) + return Curl_cf_ngtcp2_proxy_create(pcf, data, origin, peer, transport_peer, + conn, addr, + tunnel_peer, tunnel_transport); #else *pcf = NULL; (void)data; (void)conn; (void)addr; + (void)peer; + (void)transport_peer; + (void)tunnel_peer; + (void)tunnel_transport; return CURLE_NOT_BUILT_IN; #endif } +#endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */ + CURLcode Curl_conn_may_http3(struct Curl_easy *data, const struct connectdata *conn, unsigned char transport) @@ -728,19 +868,17 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, failf(data, "HTTP/3 cannot be used over UNIX domain sockets"); return CURLE_QUIC_CONNECT_ERROR; } - if(!(conn->scheme->flags & PROTOPT_SSL)) { + if(!(data->state.origin->scheme->flags & PROTOPT_SSL)) { failf(data, "HTTP/3 requested for non-HTTPS URL"); return CURLE_URL_MALFORMAT; } #ifndef CURL_DISABLE_PROXY - if(conn->bits.socksproxy) { + if(conn->socks_proxy.peer) { failf(data, "HTTP/3 is not supported over a SOCKS proxy"); return CURLE_URL_MALFORMAT; } - if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { - failf(data, "HTTP/3 is not supported over an HTTP proxy"); - return CURLE_URL_MALFORMAT; - } +#else + (void)conn; #endif return CURLE_OK; diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index 1f0a1ab5e51b..5211a9b33a66 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -39,14 +39,45 @@ CURLcode Curl_qlogdir(struct Curl_easy *data, size_t scidlen, int *qlogfdp); +CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at, + struct Curl_peer *origin, + struct Curl_peer *peer); + CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, struct connectdata *conn, struct Curl_sockaddr_ex *addr, - uint8_t transport); + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); extern struct Curl_cftype Curl_cft_http3; +#if !defined(CURL_DISABLE_PROXY) && defined(USE_PROXY_HTTP3) + +CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); + +CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer, + uint8_t transport_peer, + struct connectdata *conn, + struct Curl_sockaddr_ex *addr, + struct Curl_peer *tunnel_peer, + uint8_t tunnel_transport); + +extern struct Curl_cftype Curl_cft_h3_proxy; + +#endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */ + #else #define Curl_vquic_init() 1 #endif /* !CURL_DISABLE_HTTP && USE_HTTP3 */ diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index 82bd5b03581b..db5183d43115 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -90,14 +90,6 @@ void vquic_ctx_set_time(struct cf_quic_ctx *qctx, 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, - const uint8_t *pkt, size_t pktlen, size_t gsolen); - -CURLcode vquic_send_blocked_pkts(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx); - CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t gsolen); diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 44094e466c84..e4071caa46d4 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -57,6 +57,7 @@ #include "multiif.h" #include "select.h" #include "vssh/vssh.h" +#include "curlx/base64.h" /* for curlx_base64_encode() */ #ifdef HAVE_UNISTD_H #include @@ -109,12 +110,14 @@ static CURLcode sftp_error_to_CURLE(int err) } /* Multiple options: - * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 + * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256] is set with a SHA256 + * hash. + * 2. 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) - * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first + * 3. data->set.ssh_keyfunc callback is set. Then we do trust on first * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE * is returned by it. - * 3. none of the above. We only accept if it is present on known hosts. + * 4. none of the above. We only accept if it is present on known hosts. * * Returns SSH_OK or SSH_ERROR. */ @@ -122,8 +125,10 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) { int rc; ssh_key pubkey; - size_t hlen; - unsigned char *hash = NULL; + unsigned char *hash_sha256 = NULL; + size_t hlen_sha256; + unsigned char *hash_md5 = NULL; + size_t hlen_md5; char *found_base64 = NULL; char *known_base64 = NULL; int vstate; @@ -139,20 +144,75 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) if(rc != SSH_OK) return rc; + if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) { + const char *pubkey_sha256 = + data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; + char *fingerprint_b64 = NULL; + size_t fingerprint_b64_len; + size_t pub_pos = 0; + size_t b64_pos = 0; + + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_SHA256, + &hash_sha256, &hlen_sha256); + if(rc != SSH_OK || hlen_sha256 != 32) { + failf(data, "Denied establishing ssh session: " + "SHA256 fingerprint not available"); + goto cleanup; + } + + if(curlx_base64_encode((const uint8_t *)hash_sha256, 32, &fingerprint_b64, + &fingerprint_b64_len) != CURLE_OK) { + rc = SSH_ERROR; + goto cleanup; + } + + infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); + + /* Find the position of any = padding characters in the public key */ + while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { + pub_pos++; + } + + /* Find the position of any = padding characters in the base64 coded + * hostkey fingerprint */ + while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) { + b64_pos++; + } + + /* 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. " + "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); + curlx_free(fingerprint_b64); + rc = SSH_ERROR; + goto cleanup; + } + + curlx_free(fingerprint_b64); + + rc = SSH_OK; + goto cleanup; + } + if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { - int i; - char md5buffer[33]; const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; + char md5buffer[33]; + int i; - rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen); - if(rc != SSH_OK || hlen != 16) { + rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, + &hash_md5, &hlen_md5); + if(rc != SSH_OK || hlen_md5 != 16) { failf(data, "Denied establishing ssh session: MD5 fingerprint not available"); goto cleanup; } for(i = 0; i < 16; i++) - curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash[i]); + curl_msnprintf(&md5buffer[i * 2], 3, "%02x", hash_md5[i]); infof(data, "SSH MD5 fingerprint: %s", md5buffer); @@ -217,6 +277,8 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) keymatch = CURLKHMATCH_OK; break; case SSH_KNOWN_HOSTS_OTHER: + keymatch = CURLKHMATCH_MISMATCH; + break; case SSH_KNOWN_HOSTS_NOT_FOUND: case SSH_KNOWN_HOSTS_UNKNOWN: case SSH_KNOWN_HOSTS_ERROR: @@ -297,8 +359,10 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) /* !checksrc! disable BANNEDFUNC 1 */ free(known_base64); /* allocated by libssh, deallocate with system free */ } - if(hash) - ssh_clean_pubkey_hash(&hash); + if(hash_sha256) + ssh_clean_pubkey_hash(&hash_sha256); + if(hash_md5) + ssh_clean_pubkey_hash(&hash_md5); ssh_key_free(pubkey); if(knownhostsentry) { ssh_knownhosts_entry_free(knownhostsentry); @@ -633,7 +697,8 @@ static int myssh_auth_interactive(struct connectdata *conn, if(nprompts != 1) return SSH_ERROR; - rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); + rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, + Curl_creds_passwd(conn->creds)); if(rc < 0) return SSH_ERROR; @@ -799,7 +864,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, /* Two choices, (1) private key was given on CMD, * (2) use the "default" keys. */ if(data->set.str[STRING_SSH_PRIVATE_KEY]) { - if(sshc->pubkey && !data->set.ssl.key_passwd) { + if(sshc->pubkey && !data->set.ssl.primary.key_passwd) { rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, sshc->pubkey); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; @@ -812,7 +877,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, rc = ssh_pki_import_privkey_file(data-> set.str[STRING_SSH_PRIVATE_KEY], - data->set.ssl.key_passwd, NULL, + data->set.ssl.primary.key_passwd, NULL, NULL, &sshc->privkey); if(rc != SSH_OK) { failf(data, "Could not load private key file %s", @@ -825,7 +890,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.primary.key_passwd); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; @@ -920,7 +985,8 @@ static int myssh_in_AUTH_PASS_INIT(struct Curl_easy *data, static int myssh_in_AUTH_PASS(struct Curl_easy *data, struct ssh_conn *sshc) { - int rc = ssh_userauth_password(sshc->ssh_session, NULL, data->conn->passwd); + int rc = ssh_userauth_password(sshc->ssh_session, NULL, + Curl_creds_passwd(data->conn->creds)); if(rc == SSH_AUTH_AGAIN) return SSH_AGAIN; else if(rc == SSH_AUTH_SUCCESS) { @@ -1807,7 +1873,7 @@ static int myssh_in_TRANS_INIT(struct Curl_easy *data, struct ssh_conn *sshc, static void sshc_cleanup(struct ssh_conn *sshc) { - if(sshc->initialised) { + if(sshc->initialized) { if(sshc->sftp_file) { sftp_close(sshc->sftp_file); sshc->sftp_file = NULL; @@ -1822,8 +1888,8 @@ static void sshc_cleanup(struct ssh_conn *sshc) } /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->scp_session == NULL); + DEBUGASSERT(!sshc->ssh_session); + DEBUGASSERT(!sshc->scp_session); if(sshc->readdir_tmp) { ssh_string_free_char(sshc->readdir_tmp); @@ -1857,7 +1923,7 @@ static void sshc_cleanup(struct ssh_conn *sshc) curlx_dyn_free(&sshc->readdir_buf); curlx_safefree(sshc->readdir_linkPath); SSH_STRING_FREE_CHAR(sshc->homedir); - sshc->initialised = FALSE; + sshc->initialized = FALSE; } } @@ -2378,7 +2444,7 @@ static CURLcode myssh_statemachine(struct Curl_easy *data, if(!result && (sshc->state == SSH_STOP)) result = sshc->actualcode; CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - Curl_ssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), (int)result, *block); return result; } @@ -2403,7 +2469,7 @@ static CURLcode myssh_pollset(struct Curl_easy *data, if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); - CURL_TRC_SSH(data, "pollset, flags=%x", flags); + CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); return Curl_pollset_change(data, ps, sock, flags, 0); } /* While we still have a session, we listen incoming data. */ @@ -2502,7 +2568,7 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; curlx_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2); - sshc->initialised = TRUE; + sshc->initialized = TRUE; if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor)) return CURLE_OUT_OF_MEMORY; @@ -2545,13 +2611,13 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) sshc->ssh_session = ssh_new(); if(!sshc->ssh_session) { - failf(data, "Failure initialising ssh session"); + failf(data, "Failure initializing ssh session"); return CURLE_FAILED_INIT; } rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, (data->state.up.hostname[0] == '[') ? - data->state.up.hostname : conn->host.name); + data->state.up.hostname : conn->origin->hostname); if(rc != SSH_OK) { failf(data, "Could not set remote host"); @@ -2571,9 +2637,10 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } - if(conn->user && conn->user[0] != '\0') { - infof(data, "User: %s", conn->user); - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, conn->user); + if(Curl_creds_has_user(conn->creds)) { + infof(data, "User: %s", conn->creds->user); + rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, + conn->creds->user); if(rc != SSH_OK) { failf(data, "Could not set user"); return CURLE_FAILED_INIT; @@ -2595,9 +2662,9 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) } } - if(conn->remote_port) { + if(conn->origin->port) { rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_PORT, - &conn->remote_port); + &conn->origin->port); if(rc != SSH_OK) { failf(data, "Could not set remote port"); return CURLE_FAILED_INIT; @@ -3094,23 +3161,23 @@ void Curl_ssh_version(char *buffer, size_t buflen) * 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 */ + 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 */ }; /* diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 4e2a72269ff5..b12d69f3177a 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -57,7 +57,7 @@ #include "curlx/fopen.h" #include "vssh/vssh.h" #include "curlx/strparse.h" -#include "curlx/base64.h" /* for base64 encoding/decoding */ +#include "curlx/base64.h" /* for curlx_base64_encode() */ static const char *sftp_libssh2_strerror(unsigned long err) { @@ -104,10 +104,10 @@ static const char *sftp_libssh2_strerror(unsigned long err) case LIBSSH2_FX_QUOTA_EXCEEDED: return "User quota exceeded"; - case LIBSSH2_FX_UNKNOWN_PRINCIPLE: - return "Unknown principle"; + case LIBSSH2_FX_UNKNOWN_PRINCIPAL: + return "Unknown principal"; - case LIBSSH2_FX_LOCK_CONFlICT: + case LIBSSH2_FX_LOCK_CONFLICT: return "File lock conflict"; case LIBSSH2_FX_DIR_NOT_EMPTY: @@ -148,11 +148,11 @@ static void kbd_callback(const char *name, int name_len, #endif /* CURL_LIBSSH2_DEBUG */ if(num_prompts == 1) { struct connectdata *conn = data->conn; + const char *passwd = Curl_creds_passwd(conn->creds); /* this function must allocate memory that can be freed by libssh2, which uses the LIBSSH2_FREE_FUNC callback */ - responses[0].text = Curl_cstrdup(conn->passwd); - responses[0].length = - responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); + responses[0].text = Curl_cstrdup(passwd); + responses[0].length = responses[0].text ? curlx_uztoui(strlen(passwd)) : 0; } (void)prompts; } /* kbd_callback */ @@ -169,7 +169,7 @@ static CURLcode sftp_libssh2_error_to_CURLE(unsigned long err) case LIBSSH2_FX_PERMISSION_DENIED: case LIBSSH2_FX_WRITE_PROTECT: - case LIBSSH2_FX_LOCK_CONFlICT: + case LIBSSH2_FX_LOCK_CONFLICT: return CURLE_REMOTE_ACCESS_DENIED; case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: @@ -275,9 +275,11 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype) case LIBSSH2_HOSTKEY_TYPE_RSA: keytype = CURLKHTYPE_RSA; break; - case LIBSSH2_HOSTKEY_TYPE_DSS: +#ifdef LIBSSH2_HOSTKEY_TYPE_DSS + case LIBSSH2_HOSTKEY_TYPE_DSS: /* deprecated upstream */ keytype = CURLKHTYPE_DSS; break; +#endif #ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256 case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: keytype = CURLKHTYPE_ECDSA; @@ -305,136 +307,154 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype) static CURLcode ssh_knownhost(struct Curl_easy *data, struct ssh_conn *sshc) { + struct connectdata *conn = data->conn; + struct libssh2_knownhost *host = NULL; + const char *remotekey = NULL; + int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; + int keybit = 0; int sshkeytype = 0; size_t keylen = 0; int rc = 0; CURLcode result = CURLE_OK; - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - /* we are asked to verify the host against a file */ - struct connectdata *conn = data->conn; - struct libssh2_knownhost *host = NULL; - const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, - &keylen, &sshkeytype); - int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; - int keybit = 0; + if(!data->set.str[STRING_SSH_KNOWNHOSTS]) { + infof(data, "SSH: no knownhosts file configured"); + return CURLE_OK; + } - if(remotekey) { - /* - * A subject to figure out is what hostname we need to pass in here. - * What hostname does OpenSSH store in its file if an IDN name is - * used? - */ - enum curl_khmatch keymatch; - curl_sshkeycallback func = - data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; - struct curl_khkey knownkey; - struct curl_khkey *knownkeyp = NULL; - struct curl_khkey foundkey; - - switch(sshkeytype) { - case LIBSSH2_HOSTKEY_TYPE_RSA: - keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; - break; - case LIBSSH2_HOSTKEY_TYPE_DSS: - keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; - break; - case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: - keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; - break; - case LIBSSH2_HOSTKEY_TYPE_ED25519: - keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; - break; - default: - infof(data, "unsupported key type, cannot check knownhosts"); - keybit = 0; - break; + remotekey = libssh2_session_hostkey(sshc->ssh_session, + &keylen, &sshkeytype); + if(remotekey) { + /* + * A subject to figure out is what hostname we need to pass in here. + * What hostname does OpenSSH store in its file if an IDN name is + * used? + */ + enum curl_khmatch keymatch; + curl_sshkeycallback func = + data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; + struct curl_khkey knownkey; + struct curl_khkey *knownkeyp = NULL; + struct curl_khkey foundkey; + + switch(sshkeytype) { + case LIBSSH2_HOSTKEY_TYPE_RSA: + keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA; + break; +#ifdef LIBSSH2_HOSTKEY_TYPE_DSS + case LIBSSH2_HOSTKEY_TYPE_DSS: /* deprecated upstream */ + keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS; + break; +#endif + case LIBSSH2_HOSTKEY_TYPE_ECDSA_256: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_256; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_384: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_384; + break; + case LIBSSH2_HOSTKEY_TYPE_ECDSA_521: + keybit = LIBSSH2_KNOWNHOST_KEY_ECDSA_521; + break; + case LIBSSH2_HOSTKEY_TYPE_ED25519: + keybit = LIBSSH2_KNOWNHOST_KEY_ED25519; + break; + default: + infof(data, "SSH: unsupported host key type for knownhosts check"); + keybit = 0; + break; + } + if(!keybit) + /* no check means failure! */ + rc = CURLKHSTAT_REJECT; + else { + keycheck = libssh2_knownhost_checkp(sshc->kh, + conn->origin->hostname, + (conn->origin->port != PORT_SSH) ? + conn->origin->port : -1, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); + + infof(data, "SSH: host check %d, key: %s", keycheck, + (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ? + host->key : ""); + + /* setup 'knownkey' */ + if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { + knownkey.key = host->key; + knownkey.len = 0; + knownkey.keytype = convert_ssh2_keytype(sshkeytype); + knownkeyp = &knownkey; } - if(!keybit) - /* no check means failure! */ - rc = CURLKHSTAT_REJECT; - else { - keycheck = libssh2_knownhost_checkp(sshc->kh, - conn->host.name, - (conn->remote_port != PORT_SSH) ? - conn->remote_port : -1, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); - - infof(data, "SSH host check: %d, key: %s", keycheck, - (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ? - host->key : ""); - - /* setup 'knownkey' */ - if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { - knownkey.key = host->key; - knownkey.len = 0; - knownkey.keytype = convert_ssh2_keytype(sshkeytype); - knownkeyp = &knownkey; - } - /* setup 'foundkey' */ - foundkey.key = remotekey; - foundkey.len = keylen; - foundkey.keytype = convert_ssh2_keytype(sshkeytype); + /* setup 'foundkey' */ + foundkey.key = remotekey; + foundkey.len = keylen; + foundkey.keytype = convert_ssh2_keytype(sshkeytype); - /* - * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the - * curl_khmatch enum are ever modified, we need to introduce a - * translation table here! - */ - keymatch = (enum curl_khmatch)keycheck; + /* + * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the + * curl_khmatch enum are ever modified, we need to introduce a + * translation table here! + */ + keymatch = (enum curl_khmatch)keycheck; - /* Ask the callback how to behave */ - Curl_set_in_callback(data, TRUE); - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, FALSE); - } + /* Ask the callback how to behave */ + Curl_set_in_callback(data, TRUE); + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + Curl_set_in_callback(data, FALSE); } - else - /* no remotekey means failure! */ - rc = CURLKHSTAT_REJECT; + } + else { + /* no remotekey means failure! */ + infof(data, "SSH: host offers no public key"); + rc = CURLKHSTAT_REJECT; + } - switch(rc) { - default: /* unknown return codes is the same as reject */ - case CURLKHSTAT_REJECT: - myssh_to(data, sshc, SSH_SESSION_FREE); - FALLTHROUGH(); - case CURLKHSTAT_DEFER: - /* DEFER means bail out but keep the SSH_HOSTKEY state */ - result = CURLE_PEER_FAILED_VERIFICATION; - break; - case CURLKHSTAT_FINE_REPLACE: - /* remove old host+key that does not match */ - if(host) - libssh2_knownhost_del(sshc->kh, host); - FALLTHROUGH(); - case CURLKHSTAT_FINE: - case CURLKHSTAT_FINE_ADD_TO_FILE: - /* proceed */ - if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { + switch(rc) { + default: /* unknown return codes is the same as reject */ + case CURLKHSTAT_REJECT: + infof(data, "SSH: knownhost check failed"); + myssh_to(data, sshc, SSH_SESSION_FREE); + FALLTHROUGH(); + case CURLKHSTAT_DEFER: + /* DEFER means bail out but keep the SSH_HOSTKEY state */ + result = CURLE_PEER_FAILED_VERIFICATION; + break; + case CURLKHSTAT_FINE_REPLACE: + /* remove old host+key that does not match */ + if(host) + libssh2_knownhost_del(sshc->kh, host); + FALLTHROUGH(); + case CURLKHSTAT_FINE: + case CURLKHSTAT_FINE_ADD_TO_FILE: + /* proceed */ + if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { + int addrc; + const char *hostbuf; + char *hostport = NULL; + if(conn->origin->port != PORT_SSH) { + hostbuf = hostport = curl_maprintf("[%s]:%u", conn->origin->hostname, + conn->origin->port); + if(!hostbuf) + infof(data, "WARNING: failed allocating buffer for [host]:port"); + } + else + hostbuf = conn->origin->hostname; + if(hostbuf) { /* the found host+key did not match but has been told to be fine anyway so we add it in memory */ - int addrc = libssh2_knownhost_add(sshc->kh, - conn->host.name, NULL, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, NULL); + addrc = libssh2_knownhost_addc(sshc->kh, hostbuf, NULL, + remotekey, keylen, NULL, 0, + LIBSSH2_KNOWNHOST_TYPE_PLAIN | + LIBSSH2_KNOWNHOST_KEYENC_RAW | + keybit, NULL); if(addrc) - infof(data, "WARNING: adding the known host %s failed", - conn->host.name); + infof(data, "WARNING: adding the known host %s failed", hostbuf); else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE || rc == CURLKHSTAT_FINE_REPLACE) { /* now we write the entire in-memory list of known hosts to the @@ -449,8 +469,11 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, } } } - break; + curlx_free(hostport); } + else + infof(data, "SSH: knownhost entry matches host key"); + break; } return result; } @@ -461,11 +484,6 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]; - infof(data, "SSH MD5 public key: %s", - pubkey_md5 != NULL ? pubkey_md5 : "NULL"); - infof(data, "SSH SHA256 public key: %s", - pubkey_sha256 != NULL ? pubkey_sha256 : "NULL"); - if(pubkey_sha256) { const char *fingerprint = NULL; char *fingerprint_b64 = NULL; @@ -473,6 +491,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, size_t pub_pos = 0; size_t b64_pos = 0; + infof(data, "SSH: SHA256 public key '%s'", pubkey_sha256); /* The fingerprint points to static storage (!), do not free() it. */ fingerprint = libssh2_hostkey_hash(sshc->ssh_session, LIBSSH2_HOSTKEY_HASH_SHA256); @@ -498,7 +517,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, return CURLE_PEER_FAILED_VERIFICATION; } - infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64); + infof(data, "SSH: SHA256 fingerprint '%s'", fingerprint_b64); /* Find the position of any = padding characters in the public key */ while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) { @@ -526,13 +545,14 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, curlx_free(fingerprint_b64); - infof(data, "SHA256 checksum match"); + infof(data, "SSH: SHA256 checksum match"); } if(pubkey_md5) { char md5buffer[33]; const char *fingerprint; + infof(data, "SSH: MD5 public key '%s'", pubkey_md5); fingerprint = libssh2_hostkey_hash(sshc->ssh_session, LIBSSH2_HOSTKEY_HASH_MD5); @@ -544,7 +564,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, (unsigned char)fingerprint[i]); } - infof(data, "SSH MD5 fingerprint: %s", md5buffer); + infof(data, "SSH: MD5 fingerprint '%s'", md5buffer); } /* This does NOT verify the length of 'pubkey_md5' separately, which @@ -563,7 +583,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } - infof(data, "MD5 checksum match"); + infof(data, "SSH: MD5 checksum match"); } if(!pubkey_md5 && !pubkey_sha256) { @@ -582,8 +602,10 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, Curl_set_in_callback(data, FALSE); if(rc != CURLKHMATCH_OK) { myssh_to(data, sshc, SSH_SESSION_FREE); + failf(data, "SSH: callback failed host public key verification"); return CURLE_PEER_FAILED_VERIFICATION; } + infof(data, "SSH: verified public key via callback"); } else { myssh_to(data, sshc, SSH_SESSION_FREE); @@ -592,6 +614,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, return CURLE_OK; } else { + CURL_TRC_SSH(data, "no host key checksum given, checking knownhosts"); return ssh_knownhost(data, sshc); } } @@ -616,7 +639,9 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, 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"; +#ifdef LIBSSH2_KNOWNHOST_KEY_SSHDSS static const char hostkey_method_ssh_dss[] = "ssh-dss"; +#endif bool found = FALSE; if(sshc->kh && @@ -636,22 +661,22 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, const char *p; const char *kh_name_end = strstr(store->name, "]:"); if(!kh_name_end) { - infof(data, "Invalid host pattern %s in %s", + infof(data, "SSH: invalid host pattern %s in %s", store->name, data->set.str[STRING_SSH_KNOWNHOSTS]); continue; } 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_end && (port == conn->origin->port))) { kh_name_size = strlen(store->name) - 1 - strlen(kh_name_end); - if(strncmp(store->name + 1, - conn->host.name, kh_name_size) == 0) { + if(!strncmp(store->name + 1, conn->origin->hostname, + kh_name_size)) { found = TRUE; break; } } } - else if(strcmp(store->name, conn->host.name) == 0) { + else if(!strcmp(store->name, conn->origin->hostname)) { found = TRUE; break; } @@ -666,8 +691,8 @@ 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]); + infof(data, "SSH: found host '%s' in '%s'", + conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); switch(store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK) { case LIBSSH2_KNOWNHOST_KEY_ED25519: @@ -685,19 +710,21 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, case LIBSSH2_KNOWNHOST_KEY_SSHRSA: hostkey_method = hostkey_method_ssh_rsa_all; break; - case LIBSSH2_KNOWNHOST_KEY_SSHDSS: +#ifdef LIBSSH2_KNOWNHOST_KEY_SSHDSS + case LIBSSH2_KNOWNHOST_KEY_SSHDSS: /* deprecated upstream */ hostkey_method = hostkey_method_ssh_dss; break; +#endif case LIBSSH2_KNOWNHOST_KEY_RSA1: failf(data, "Found host key type RSA1 which is not supported"); return CURLE_SSH; default: - failf(data, "Unknown host key type: %i", + failf(data, "Unknown host key type: %d", (store->typemask & LIBSSH2_KNOWNHOST_KEY_MASK)); return CURLE_SSH; } - infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method); + infof(data, "SSH: set '%s' as hostkey type", hostkey_method); rc = libssh2_session_method_pref(sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method); if(rc) { @@ -709,8 +736,8 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, } } else { - infof(data, "Did not find host %s in %s", - conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]); + infof(data, "SSH: did not find host '%s' in '%s'", + conn->origin->hostname, data->set.str[STRING_SSH_KNOWNHOSTS]); } } @@ -778,7 +805,7 @@ static CURLcode sftp_quote(struct Curl_easy *data, cp = strchr(cmd, ' '); if(!cp) { failf(data, "Syntax error command '%s', missing parameter", cmd); - return result; + return CURLE_QUOTE_ERROR; } /* @@ -1077,7 +1104,7 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, sshc->authed = FALSE; if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && - (strstr(sshc->authlist, "publickey") != NULL)) { + strstr(sshc->authlist, "publickey")) { bool out_of_memory = FALSE; sshc->rsa_pub = sshc->rsa = NULL; @@ -1146,13 +1173,13 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - sshc->passphrase = data->set.ssl.key_passwd; + sshc->passphrase = data->set.ssl.primary.key_passwd; if(!sshc->passphrase) sshc->passphrase = ""; if(sshc->rsa_pub) - infof(data, "Using SSH public key file '%s'", sshc->rsa_pub); - infof(data, "Using SSH private key file '%s'", sshc->rsa); + infof(data, "SSH: trying public key file '%s'", sshc->rsa_pub); + infof(data, "SSH: trying private key file '%s'", sshc->rsa); myssh_to(data, sshc, SSH_AUTH_PKEY); } @@ -1180,7 +1207,7 @@ static CURLcode sftp_quote_stat(struct Curl_easy *data, sshc->acceptfail = TRUE; } - if(!!strncmp(cmd, "chmod", 5)) { + if(strncmp(cmd, "chmod", 5)) { /* Since chown and chgrp only set owner OR group but libssh2 wants to set * them both at once, we need to obtain the current ownership first. This * takes an extra protocol round trip. @@ -1354,7 +1381,7 @@ static CURLcode sftp_download_stat(struct Curl_easy *data, if(data->req.size == 0) { /* no data to transfer */ Curl_xfer_setup_nop(data); - infof(data, "File already completely downloaded"); + infof(data, "SSH: file already completely downloaded"); myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1496,15 +1523,15 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, * Therefore always specify it here. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); sshc->authlist = libssh2_userauth_list(sshc->ssh_session, - conn->user, - curlx_uztoui(strlen(conn->user))); + user, curlx_uztoui(strlen(user))); if(!sshc->authlist) { int rc; if(libssh2_userauth_authenticated(sshc->ssh_session)) { sshc->authed = TRUE; - infof(data, "SSH user accepted with no authentication"); + infof(data, "SSH: user accepted with no authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } @@ -1515,7 +1542,7 @@ static CURLcode ssh_state_authlist(struct Curl_easy *data, 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: host offers authentication via: %s", sshc->authlist); myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); return CURLE_OK; @@ -1527,11 +1554,11 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, /* The function below checks if the files exists, no need to stat() here. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); int rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), + user, + curlx_uztoui(strlen(user)), sshc->rsa_pub, sshc->rsa, sshc->passphrase); if(rc == LIBSSH2_ERROR_EAGAIN) @@ -1542,7 +1569,7 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized SSH public key authentication"); + infof(data, "SSH: authenticated via publickey"); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1556,7 +1583,7 @@ static CURLcode ssh_state_auth_pkey(struct Curl_easy *data, else { (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0); } - infof(data, "SSH public key authentication failed: %s", err_msg); + infof(data, "SSH: publickey authentication denied: %s", err_msg); myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } return CURLE_OK; @@ -1566,7 +1593,7 @@ static CURLcode ssh_state_auth_pass_init(struct Curl_easy *data, struct ssh_conn *sshc) { if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) && - (strstr(sshc->authlist, "password") != NULL)) { + strstr(sshc->authlist, "password")) { myssh_to(data, sshc, SSH_AUTH_PASS); } else { @@ -1579,18 +1606,20 @@ static CURLcode ssh_state_auth_pass(struct Curl_easy *data, struct ssh_conn *sshc) { struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); + const char *passwd = Curl_creds_passwd(conn->creds); int rc = - libssh2_userauth_password_ex(sshc->ssh_session, conn->user, - curlx_uztoui(strlen(conn->user)), - conn->passwd, - curlx_uztoui(strlen(conn->passwd)), + libssh2_userauth_password_ex(sshc->ssh_session, user, + curlx_uztoui(strlen(user)), + passwd, + curlx_uztoui(strlen(passwd)), NULL); if(rc == LIBSSH2_ERROR_EAGAIN) { return CURLE_AGAIN; } if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized password authentication"); + infof(data, "SSH: initialized password authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1603,7 +1632,7 @@ static CURLcode ssh_state_auth_host_init(struct Curl_easy *data, struct ssh_conn *sshc) { if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) && - (strstr(sshc->authlist, "hostbased") != NULL)) { + strstr(sshc->authlist, "hostbased")) { myssh_to(data, sshc, SSH_AUTH_HOST); } else { @@ -1617,15 +1646,16 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, { int rc = 0; if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) && - (strstr(sshc->authlist, "publickey") != NULL)) { + strstr(sshc->authlist, "publickey")) { + infof(data, "SSH: trying publickey authentication via agent"); /* Connect to the ssh-agent */ /* The agent could be shared by a curl thread i believe but nothing obvious as keys can be added/removed at any time */ if(!sshc->ssh_agent) { sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session); if(!sshc->ssh_agent) { - infof(data, "Could not create agent object"); + infof(data, "SSH: could not create agent object"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); return CURLE_OK; @@ -1636,7 +1666,7 @@ static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc < 0) { - infof(data, "Failure connecting to agent"); + infof(data, "SSH: failure connecting to agent"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { @@ -1656,7 +1686,7 @@ static CURLcode ssh_state_auth_agent_list(struct Curl_easy *data, if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc < 0) { - infof(data, "Failure requesting identities to agent"); + infof(data, "SSH: failure requesting identities to agent"); myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else { @@ -1679,8 +1709,11 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, return CURLE_AGAIN; if(rc == 0) { - struct connectdata *conn = data->conn; - rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user, + CURL_TRC_SSH(data, "[SSH_AUTH_AGENT_LIST] auth user '%s' for key '%s'", + Curl_creds_user(data->conn->creds), + sshc->sshagent_identity->comment); + rc = libssh2_agent_userauth(sshc->ssh_agent, + Curl_creds_user(data->conn->creds), sshc->sshagent_identity); if(rc < 0) { @@ -1694,13 +1727,15 @@ static CURLcode ssh_state_auth_agent(struct Curl_easy *data, } if(rc < 0) - infof(data, "Failure requesting identities to agent"); + infof(data, "SSH: failure requesting identities to agent"); else if(rc == 1) - infof(data, "No identity would match"); + infof(data, "SSH: no agent identity would match"); if(rc == LIBSSH2_ERROR_NONE) { sshc->authed = TRUE; - infof(data, "Agent based authentication successful"); + infof(data, "SSH: agent authenticated user '%s' with key '%s'", + Curl_creds_user(data->conn->creds), + sshc->sshagent_identity->comment); myssh_to(data, sshc, SSH_AUTH_DONE); } else { @@ -1713,7 +1748,7 @@ 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)) { + strstr(sshc->authlist, "keyboard-interactive")) { myssh_to(data, sshc, SSH_AUTH_KEY); } else { @@ -1727,18 +1762,17 @@ static CURLcode ssh_state_auth_key(struct Curl_easy *data, { /* Authentication failed. Continue with keyboard-interactive now. */ struct connectdata *conn = data->conn; + const char *user = Curl_creds_user(conn->creds); int rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session, - conn->user, - curlx_uztoui( - strlen(conn->user)), + user, curlx_uztoui(strlen(user)), &kbd_callback); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; if(rc == 0) { sshc->authed = TRUE; - infof(data, "Initialized keyboard interactive authentication"); + infof(data, "SSH: initialized keyboard interactive authentication"); myssh_to(data, sshc, SSH_AUTH_DONE); return CURLE_OK; } @@ -1758,7 +1792,7 @@ static CURLcode ssh_state_auth_done(struct Curl_easy *data, /* * At this point we have an authenticated ssh session. */ - infof(data, "Authentication complete"); + infof(data, "SSH: authentication complete"); Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ @@ -1769,7 +1803,7 @@ static CURLcode ssh_state_auth_done(struct Curl_easy *data, myssh_to(data, sshc, SSH_SFTP_INIT); return CURLE_OK; } - infof(data, "SSH CONNECT phase done"); + infof(data, "SSH: connection established"); myssh_to(data, sshc, SSH_STOP); return CURLE_OK; } @@ -1863,7 +1897,7 @@ static CURLcode ssh_state_sftp_quote_init(struct Curl_easy *data, } if(data->set.quote) { - infof(data, "Sending quote commands"); + infof(data, "SSH: sending quote commands"); sshc->quote_item = data->set.quote; myssh_to(data, sshc, SSH_SFTP_QUOTE); } @@ -1877,7 +1911,7 @@ static CURLcode ssh_state_sftp_postquote_init(struct Curl_easy *data, struct ssh_conn *sshc) { if(data->set.postquote) { - infof(data, "Sending quote commands"); + infof(data, "SSH: sending quote commands"); sshc->quote_item = data->set.postquote; myssh_to(data, sshc, SSH_SFTP_QUOTE); } @@ -2585,12 +2619,12 @@ static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, } /* 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); + DEBUGASSERT(!sshc->ssh_session); + DEBUGASSERT(!sshc->ssh_channel); + DEBUGASSERT(!sshc->sftp_session); + DEBUGASSERT(!sshc->sftp_handle); + DEBUGASSERT(!sshc->kh); + DEBUGASSERT(!sshc->ssh_agent); curlx_safefree(sshc->rsa_pub); curlx_safefree(sshc->rsa); @@ -2692,7 +2726,7 @@ static CURLcode ssh_state_sftp_create_dirs(struct Curl_easy *data, sshc->slash_pos = strchr(sshc->slash_pos, '/'); if(sshc->slash_pos) { *sshc->slash_pos = 0; - infof(data, "Creating directory '%s'", sshp->path); + infof(data, "SFTP: creating directory '%s'", sshp->path); myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); return CURLE_OK; } @@ -2817,8 +2851,7 @@ static CURLcode ssh_state_scp_send_eof(struct Curl_easy *data, 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", + infof(data, "Failed to send libssh2 channel EOF: %d %s", rc, err_msg); } } @@ -2837,8 +2870,7 @@ static CURLcode ssh_state_scp_wait_eof(struct Curl_easy *data, 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); + infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); } } myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE); @@ -2857,8 +2889,7 @@ static CURLcode ssh_state_scp_wait_close(struct Curl_easy *data, 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); + infof(data, "Channel failed to close: %d %s", rc, err_msg); } } myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); @@ -2877,9 +2908,7 @@ static CURLcode ssh_state_scp_channel_free( 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); + infof(data, "Failed to free libssh2 scp subsystem: %d %s", rc, err_msg); } sshc->ssh_channel = NULL; } @@ -3162,7 +3191,7 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, result = CURLE_OK; } CURL_TRC_SSH(data, "[%s] statemachine() -> %d, block=%d", - Curl_ssh_statename(sshc->state), result, *block); + Curl_ssh_statename(sshc->state), (int)result, *block); return result; } @@ -3188,7 +3217,7 @@ static CURLcode ssh_pollset(struct Curl_easy *data, if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); - CURL_TRC_SSH(data, "pollset, flags=%x", flags); + CURL_TRC_SSH(data, "pollset, flags=%x", (unsigned int)flags); return Curl_pollset_change(data, ps, sock, flags, 0); } /* While we still have a session, we listen incoming data. */ @@ -3445,16 +3474,16 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) break; } if(crypto_str) - infof(data, "libssh2 cryptography backend: %s", crypto_str); + infof(data, "SSH: libssh2 cryptography backend: %s", crypto_str); } #endif if(!sshc) return CURLE_FAILED_INIT; - infof(data, "User: '%s'", conn->user); + infof(data, "SSH: user '%s'", Curl_creds_user(conn->creds)); #ifdef CURL_LIBSSH2_DEBUG - infof(data, "Password: %s", conn->passwd); + infof(data, "SSH: password %s", Curl_creds_passwd(conn->creds)); sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ @@ -3465,7 +3494,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) my_libssh2_realloc, data); if(!sshc->ssh_session) { - failf(data, "Failure initialising ssh session"); + failf(data, "Failure initializing ssh session"); return CURLE_FAILED_INIT; } @@ -3492,7 +3521,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) */ #if LIBSSH2_VERSION_NUM >= 0x010b01 - infof(data, "Uses HTTPS proxy"); + infof(data, "SSH: using HTTPS proxy"); #if defined(__clang__) && __clang_major__ >= 16 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wcast-function-type-strict" @@ -3526,7 +3555,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) sshrecv.recvptr = ssh_tls_recv; sshsend.sendptr = ssh_tls_send; - infof(data, "Uses HTTPS proxy"); + infof(data, "SSH: using HTTPS proxy"); libssh2_session_callback_set(sshc->ssh_session, LIBSSH2_CALLBACK_RECV, sshrecv.recvp); libssh2_session_callback_set(sshc->ssh_session, @@ -3551,7 +3580,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) if(data->set.ssh_compression && libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0) { - infof(data, "Failed to enable compression for ssh session"); + infof(data, "SSH: failed to enable compression for session"); } if(data->set.str[STRING_SSH_KNOWNHOSTS]) { @@ -3568,14 +3597,14 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done) data->set.str[STRING_SSH_KNOWNHOSTS], LIBSSH2_KNOWNHOST_FILE_OPENSSH); if(rc < 0) - infof(data, "Failed to read known hosts from %s", + infof(data, "SSH: failed to read known hosts from %s", data->set.str[STRING_SSH_KNOWNHOSTS]); } #ifdef CURL_LIBSSH2_DEBUG libssh2_trace(sshc->ssh_session, ~0); - infof(data, "SSH socket: %d", (int)sock); -#endif /* CURL_LIBSSH2_DEBUG */ + infof(data, "SSH: socket %d", (int)sock); +#endif myssh_to(data, sshc, SSH_INIT); @@ -3823,7 +3852,7 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, 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); + CURL_TRC_SSH(data, "DISCONNECT is done -> %d", (int)result); } sshc_cleanup(sshc, data, TRUE); } diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index 44e72ab802bf..de76c8a253d0 100644 --- a/lib/vssh/ssh.h +++ b/lib/vssh/ssh.h @@ -195,7 +195,7 @@ struct ssh_conn { const char *readdir_filename; /* points within readdir_attrs */ const char *readdir_longentry; char *readdir_tmp; - BIT(initialised); + BIT(initialized); #elif defined(USE_LIBSSH2) LIBSSH2_SESSION *ssh_session; /* Secure Shell session */ LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */ @@ -229,9 +229,9 @@ struct ssh_conn { /* Feature detection based on version numbers to better work with non-configure platforms */ -#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010208) -#error "SCP/SFTP protocols require libssh2 1.2.8 or later" -/* 1.2.8 was released on April 5 2011 */ +#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010900) +#error "SCP/SFTP protocols require libssh2 1.9.0 or greater" +/* 1.9.0 was released on June 20 2019 */ #endif #endif /* USE_LIBSSH2 */ diff --git a/lib/vtls/apple.c b/lib/vtls/apple.c index e28a40cc0e0a..2e132f29772b 100644 --- a/lib/vtls/apple.c +++ b/lib/vtls/apple.c @@ -102,7 +102,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, if(conn_config->verifyhost) { host_str = CFStringCreateWithCString(NULL, - peer->sni ? peer->sni : peer->hostname, kCFStringEncodingUTF8); + peer->sni ? peer->sni : peer->origin->hostname, kCFStringEncodingUTF8); if(!host_str) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -208,7 +208,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, status = SecTrustSetOCSPResponse(trust, ocspdata); CFRelease(ocspdata); if(status != noErr) { - failf(data, "Apple SecTrust: failed to set OCSP response: %i", + failf(data, "Apple SecTrust: failed to set OCSP response: %d", (int)status); result = CURLE_PEER_FAILED_VERIFICATION; goto out; @@ -238,10 +238,8 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, err_desc = curlx_malloc(size + 1); if(err_desc) { if(!CFStringGetCString(error_ref, err_desc, size, - kCFStringEncodingUTF8)) { - curlx_free(err_desc); - err_desc = NULL; - } + kCFStringEncodingUTF8)) + curlx_safefree(err_desc); } } infof(data, "Apple SecTrust failure %ld%s%s", code, @@ -256,7 +254,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf, status = SecTrustEvaluate(trust, &sec_result); if(status != noErr) { - failf(data, "Apple SecTrust verification failed: error %i", (int)status); + failf(data, "Apple SecTrust verification failed: error %d", (int)status); result = CURLE_PEER_FAILED_VERIFICATION; } else if((sec_result == kSecTrustResultUnspecified) || diff --git a/lib/vtls/cipher_suite.c b/lib/vtls/cipher_suite.c index 1fc6e9f8c6f9..197055fc0de5 100644 --- a/lib/vtls/cipher_suite.c +++ b/lib/vtls/cipher_suite.c @@ -639,7 +639,7 @@ uint16_t Curl_cipher_suite_lookup_id(const char *cs_str, size_t cs_len) if(cs_len > 0 && cs_str_to_zip(cs_str, cs_len, zip) == 0) { for(i = 0; i < CS_LIST_LEN; i++) { - if(memcmp(cs_list[i].zip, zip, sizeof(zip)) == 0) + if(!memcmp(cs_list[i].zip, zip, sizeof(zip))) return cs_list[i].id; } } diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 9b6a4fab2e39..1a7a4a1e5bbc 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -26,7 +26,7 @@ * but vtls.c should ever call or use these functions. * * Note: do not use the GnuTLS' *_t variable type names in this source code, - * since they were not present in 1.0.X. + * since they were not present in 1.0.x. */ #include "curl_setup.h" @@ -37,6 +37,7 @@ #include #include #include +#include #include "urldata.h" #include "curl_trc.h" @@ -71,7 +72,7 @@ static void tls_log_func(int level, const char *str) #endif #undef CURL_GNUTLS_EARLY_DATA -#if GNUTLS_VERSION_NUMBER >= 0x03060d +#if GNUTLS_VERSION_NUMBER >= 0x030702 #define CURL_GNUTLS_EARLY_DATA #endif @@ -94,7 +95,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen) DEBUGASSERT(data); result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ @@ -127,7 +128,8 @@ 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, %zu", blen, result, nread); + CURL_TRC_CF(data, cf, "gtls_pull(len=%zu) -> %d, %zu", blen, (int)result, + nread); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ @@ -203,12 +205,12 @@ static gnutls_datum_t load_file(const char *file) f = curlx_fopen(file, "rb"); if(!f) return loaded_file; - if(fseek(f, 0, SEEK_END) != 0) + if(fseek(f, 0, SEEK_END)) goto out; filelen = ftell(f); if(filelen < 0 || filelen > CURL_MAX_INPUT_LENGTH) goto out; - if(fseek(f, 0, SEEK_SET) != 0) + if(fseek(f, 0, SEEK_SET)) goto out; ptr = curlx_malloc((size_t)filelen); if(!ptr) @@ -322,12 +324,12 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type) "+GROUP-SECP256R1:+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ "%DISABLE_TLS13_COMPAT_MODE" -static CURLcode -gnutls_set_ssl_version_min_max(struct Curl_easy *data, - struct ssl_peer *peer, - struct ssl_primary_config *conn_config, - const char **prioritylist, - bool tls13support) +static CURLcode gnutls_set_ssl_version_min_max( + struct Curl_easy *data, + struct ssl_peer *peer, + struct ssl_primary_config *conn_config, + const char **prioritylist, + bool tls13support) { long ssl_version = conn_config->version; long ssl_version_max = conn_config->version_max; @@ -408,7 +410,8 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data, rc = gnutls_certificate_allocate_credentials(&shared->creds); if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); + failf(data, "gnutls_certificate_allocate_credentials() failed: %s", + gnutls_strerror(rc)); curlx_free(shared); return CURLE_SSL_CONNECT_ERROR; } @@ -692,6 +695,24 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, } #ifdef CURL_GNUTLS_EARLY_DATA +static int gtls_get_ietf_proto(gnutls_session_t session) +{ + switch(gnutls_protocol_get_version(session)) { + case GNUTLS_SSL3: + return CURL_IETF_PROTO_SSL3; + case GNUTLS_TLS1_0: + return CURL_IETF_PROTO_TLS1; + case GNUTLS_TLS1_1: + return CURL_IETF_PROTO_TLS1_1; + case GNUTLS_TLS1_2: + return CURL_IETF_PROTO_TLS1_2; + case GNUTLS_TLS1_3: + return CURL_IETF_PROTO_TLS1_3; + default: + return CURL_IETF_PROTO_UNKNOWN; + } +} + CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, struct Curl_easy *data, const char *ssl_peer_key, @@ -740,7 +761,7 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, } result = Curl_ssl_session_create2(sdata, sdata_len, - Curl_glts_get_ietf_proto(session), + gtls_get_ietf_proto(session), alpn, valid_until, earlydata_max, qtp_clone, quic_tp_len, &sc_session); @@ -753,24 +774,6 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, } #endif -int Curl_glts_get_ietf_proto(gnutls_session_t session) -{ - switch(gnutls_protocol_get_version(session)) { - case GNUTLS_SSL3: - return CURL_IETF_PROTO_SSL3; - case GNUTLS_TLS1_0: - return CURL_IETF_PROTO_TLS1; - case GNUTLS_TLS1_1: - return CURL_IETF_PROTO_TLS1_1; - case GNUTLS_TLS1_2: - return CURL_IETF_PROTO_TLS1_2; - case GNUTLS_TLS1_3: - return CURL_IETF_PROTO_TLS1_3; - default: - return CURL_IETF_PROTO_UNKNOWN; - } -} - #ifdef CURL_GNUTLS_EARLY_DATA static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -839,7 +842,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 our own */ if(!curlx_dyn_len(&buf)) { /* not added yet */ result = curlx_dyn_add(&buf, priority); if(result) @@ -995,10 +998,11 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, if(result) return result; } - if(ssl_config->cert_type && curl_strequal(ssl_config->cert_type, "P12")) { + if(ssl_config->primary.cert_type && + curl_strequal(ssl_config->primary.cert_type, "P12")) { rc = gnutls_certificate_set_x509_simple_pkcs12_file( gtls->shared_creds->creds, config->clientcert, GNUTLS_X509_FMT_DER, - ssl_config->key_passwd ? ssl_config->key_passwd : ""); + ssl_config->primary.key_passwd ? ssl_config->primary.key_passwd : ""); if(rc != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 potentially-encrypted key or certificate " @@ -1016,14 +1020,15 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, rc = gnutls_certificate_set_x509_key_file2( gtls->shared_creds->creds, config->clientcert, - ssl_config->key ? ssl_config->key : config->clientcert, - gnutls_do_file_type(ssl_config->cert_type), - ssl_config->key_passwd, + ssl_config->primary.key ? ssl_config->primary.key : + config->clientcert, + gnutls_do_file_type(ssl_config->primary.cert_type), + ssl_config->primary.key_passwd, supported_key_encryption_algorithms); if(rc != GNUTLS_E_SUCCESS) { failf(data, "error reading X.509 %skey file: %s", - ssl_config->key_passwd ? "potentially-encrypted " : "", + ssl_config->primary.key_passwd ? "potentially-encrypted " : "", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } @@ -1361,11 +1366,12 @@ static void gtls_msg_verify_result(struct Curl_easy *data, if(!was_verified) { if(needs_verified) { failf(data, "SSL: certificate subject name (%s) does not match " - "target hostname '%s'", certname, peer->dispname); + "target hostname '%s'", certname, + peer->origin->user_hostname); } else infof(data, " common name: %s (does not match '%s')", - certname, peer->dispname); + certname, peer->origin->user_hostname); } else infof(data, " common name: %s (matched)", certname); @@ -1425,7 +1431,10 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, { gnutls_ocsp_resp_t ocsp_resp = NULL; gnutls_datum_t status_request; - gnutls_ocsp_cert_status_t status; + gnutls_certificate_credentials_t creds = NULL; + gnutls_x509_trust_list_t tlist = NULL; + unsigned int verify_status = 0; + gnutls_ocsp_cert_status_t status = GNUTLS_OCSP_CERT_UNKNOWN; gnutls_x509_crl_reason_t reason; CURLcode result = CURLE_OK; int rc; @@ -1457,6 +1466,20 @@ static CURLcode gtls_verify_ocsp_status(struct Curl_easy *data, goto out; } + if(!gnutls_credentials_get(session, GNUTLS_CRD_CERTIFICATE, (void **)&creds)) + gnutls_certificate_get_trust_list(creds, &tlist); + if(!tlist) { + failf(data, "OCSP response signature verification failed"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + rc = gnutls_ocsp_resp_verify(ocsp_resp, tlist, &verify_status, 0); + if(rc < 0 || verify_status) { + failf(data, "OCSP response signature verification failed"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, &status, NULL, NULL, NULL, &reason); @@ -1555,21 +1578,75 @@ static CURLcode gtls_chain_get_der(struct Curl_cfilter *cf, *pder_len = (size_t)chain->certs[i].size; return CURLE_OK; } +#endif /* USE_APPLE_SECTRUST */ -static CURLcode glts_apple_verify(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - struct gtls_cert_chain *chain, - bool *pverified) +/* This function verifies the peer's certificate and returns CURLE_OK on + success or an appropriate CURLcode on error. The certificate verification + status bitmask (trusted, invalid etc.) is stored in + ssl_config->certverifyresult as one or more gnutls_certificate_status_t + enumerated elements bitwise or'd. */ +static CURLcode gtls_verify_cert(struct Curl_easy *data, + struct ssl_primary_config *config, + struct ssl_config_data *ssl_config, + gnutls_session_t session, + struct Curl_cfilter *cf, + struct ssl_peer *peer, + struct gtls_cert_chain *chain) { - CURLcode result; + bool verified = FALSE; + unsigned int verify_status = 0; + long * const certverifyresult = &ssl_config->certverifyresult; + int rc = gnutls_certificate_verify_peers2(session, &verify_status); + if(rc < 0) { + failf(data, "server cert verify failed: %d", rc); + *certverifyresult = rc; + return CURLE_SSL_CONNECT_ERROR; + } + *certverifyresult = verify_status; + verified = !(verify_status & GNUTLS_CERT_INVALID); + if(verified) + infof(data, " SSL certificate verified by GnuTLS"); - result = Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, - gtls_chain_get_der, chain, NULL, 0); - *pverified = !result; - return result; +#ifdef USE_APPLE_SECTRUST + if(!verified && ssl_config->native_ca_store) { + CURLcode result = + Curl_vtls_apple_verify(cf, data, peer, chain->num_certs, + gtls_chain_get_der, chain, NULL, 0); + if(result && (result != CURLE_PEER_FAILED_VERIFICATION)) + return result; /* unexpected error */ + verified = !result; + if(verified) { + infof(data, "SSL certificate verified via Apple SecTrust."); + *certverifyresult = 0; + } + } +#else + (void)cf; + (void)peer; + (void)chain; +#endif + + 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"); + + return CURLE_PEER_FAILED_VERIFICATION; + } + return CURLE_OK; } -#endif /* USE_APPLE_SECTRUST */ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1643,58 +1720,10 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, } if(config->verifypeer) { - 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); - *certverifyresult = rc; - 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"); - -#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; - } - } -#endif - - 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; + result = gtls_verify_cert(data, config, ssl_config, session, + cf, peer, &chain); + if(result) goto out; - } } else infof(data, " SSL certificate verification SKIPPED"); @@ -1791,17 +1820,22 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, } 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) { + failf(data, "failed to import issuer certificate (%s) (Issuer Cert: %s)", + gnutls_strerror(rc), config->issuercert); + result = CURLE_SSL_ISSUER_ERROR; + goto out; + } + rc = (int)gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); if(rc <= 0) { - failf(data, "server certificate issuer check failed (IssuerCert: %s)", - config->issuercert ? config->issuercert : "none"); + failf(data, "server certificate issuer check failed (Issuer Cert: %s)", + config->issuercert); result = CURLE_SSL_ISSUER_ERROR; goto out; } infof(data, " SSL certificate issuer check OK (Issuer Cert: %s)", - config->issuercert ? config->issuercert : "none"); + config->issuercert); } /* This function checks if the given certificate's subject matches the @@ -1815,7 +1849,7 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf, IP addresses) */ rc = (int)gnutls_x509_crt_check_hostname(x509_cert, peer->sni ? peer->sni : - peer->hostname); + peer->origin->hostname); result = (!rc && config->verifyhost) ? CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; gtls_msg_verify_result(data, peer, x509_cert, rc, config->verifyhost); @@ -2014,7 +2048,8 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, } *done = ((connssl->state == ssl_connection_complete) || (connssl->state == ssl_connection_deferred)); - CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", (int)result, + *done); return result; } @@ -2089,7 +2124,7 @@ static CURLcode gtls_send(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "gtls_send(len=%zu) -> %d, %zu", - blen, result, *pnwritten); + blen, (int)result, *pnwritten); return result; } @@ -2234,7 +2269,8 @@ static CURLcode gtls_recv(struct Curl_cfilter *cf, } out: - CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> 0, %zd", blen, nread); + CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> %d, %zd", blen, + (int)result, nread); return result; } @@ -2253,15 +2289,25 @@ static CURLcode gtls_random(struct Curl_easy *data, return rc ? CURLE_FAILED_INIT : CURLE_OK; } -static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode gtls_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum, /* output */ size_t sha256len) { struct sha256_ctx SHA256pw; sha256_init(&SHA256pw); - sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + sha256_update(&SHA256pw, ilen, input); + len -= ilen; + input += ilen; + } while(len); +#if NETTLE_VERSION_MAJOR >= 4 + (void)sha256len; + sha256_digest(&SHA256pw, sha256sum); +#else sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum); +#endif return CURLE_OK; } diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index 49106dd869af..4c662003eb82 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -48,8 +48,6 @@ struct ssl_peer; struct ssl_connect_data; struct Curl_ssl_session; -int Curl_glts_get_ietf_proto(gnutls_session_t session); - struct gtls_shared_creds { gnutls_certificate_credentials_t creds; char *CAfile; /* CAfile path used to generate X509 store */ diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c index 4ae2387a7a72..094bf69db728 100644 --- a/lib/vtls/keylog.c +++ b/lib/vtls/keylog.c @@ -66,7 +66,7 @@ void Curl_tls_keylog_close(void) bool Curl_tls_keylog_enabled(void) { - return keylog_file_fp != NULL; + return !!keylog_file_fp; } const char *Curl_tls_keylog_file_name(void) @@ -102,9 +102,10 @@ bool 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 + diff --git a/lib/vtls/keylog.h b/lib/vtls/keylog.h index 68ded4769d10..b09fcc6f4d4c 100644 --- a/lib/vtls/keylog.h +++ b/lib/vtls/keylog.h @@ -61,9 +61,10 @@ const char *Curl_tls_keylog_file_name(void); * Appends a key log file entry. * 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[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); /* * 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 e2822668f085..57727613ae9a 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -141,7 +141,7 @@ static int mbedtls_bio_cf_write(void *bio, result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %d, %zu", - blen, result, nwritten); + blen, (int)result, nwritten); if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_WRITE; return result ? -1 : (int)nwritten; @@ -163,7 +163,7 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) result = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &nread); CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %d, %zu", - blen, result, nread); + blen, (int)result, nread); if(result == CURLE_AGAIN) return MBEDTLS_ERR_SSL_WANT_READ; /* nread is never larger than int here */ @@ -179,10 +179,10 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) #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) -static CURLcode -mbed_set_ssl_version_min_max(struct Curl_easy *data, - struct mbed_ssl_backend_data *backend, - struct ssl_primary_config *conn_config) +static CURLcode mbed_set_ssl_version_min_max( + struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + struct ssl_primary_config *conn_config) { mbedtls_ssl_protocol_version ver_min = #ifdef MBEDTLS_SSL_PROTO_TLS1_2 @@ -275,15 +275,15 @@ static uint16_t mbed_cipher_suite_walk_str(const char **str, const char **end) return id; } #else -#define mbed_cipher_suite_get_str Curl_cipher_suite_get_str +#define mbed_cipher_suite_get_str Curl_cipher_suite_get_str #define mbed_cipher_suite_walk_str Curl_cipher_suite_walk_str #endif -static CURLcode -mbed_set_selected_ciphers(struct Curl_easy *data, - struct mbed_ssl_backend_data *backend, - const char *ciphers12, - const char *ciphers13) +static CURLcode mbed_set_selected_ciphers( + struct Curl_easy *data, + struct mbed_ssl_backend_data *backend, + const char *ciphers12, + const char *ciphers13) { const char *ciphers = ciphers12; const int *supported; @@ -307,7 +307,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, /* Add default TLSv1.3 ciphers to selection */ for(j = 0; j < supported_len; j++) { uint16_t id = (uint16_t)supported[j]; - if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) != 0) + if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6)) continue; selected[count++] = id; @@ -360,7 +360,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, /* Add default TLSv1.2 ciphers to selection */ for(j = 0; j < supported_len; j++) { uint16_t id = (uint16_t)supported[j]; - if(strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6) == 0) + if(!strncmp(mbedtls_ssl_get_ciphersuite_name(id), "TLS1-3", 6)) continue; /* No duplicates allowed (so selected cannot overflow) */ @@ -464,7 +464,8 @@ static int mbed_verify_cb(void *ptr, mbedtls_x509_crt *crt, mbedtls_x509_crt_verify_info(buf, sizeof(buf), "", *flags); failf(data, "mbedTLS: %s", buf); #else - failf(data, "mbedTLS: certificate verification error 0x%08x", *flags); + failf(data, "mbedTLS: certificate verification error 0x%08x", + (unsigned int)*flags); #endif } @@ -486,7 +487,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, const char * const ssl_capath = conn_config->CApath; #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; + const char * const ssl_cert_type = ssl_config->primary.cert_type; #endif int ret = -1; char errorbuf[128]; @@ -529,7 +530,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error importing CA cert blob: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -541,7 +542,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CA cert file %s: (-0x%04X) %s", - ssl_cafile, -ret, errorbuf); + ssl_cafile, (unsigned int)-ret, errorbuf); return CURLE_SSL_CACERT_BADFILE; } #else @@ -557,7 +558,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, if(ret < 0) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CA cert path %s: (-0x%04X) %s", - ssl_capath, -ret, errorbuf); + ssl_capath, (unsigned int)-ret, errorbuf); if(verifypeer) return CURLE_SSL_CACERT_BADFILE; @@ -581,7 +582,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, 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; + const char * const ssl_cert_type = ssl_config->primary.cert_type; #endif int ret = -1; char errorbuf[128]; @@ -595,7 +596,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading client cert file %s: (-0x%04X) %s", - ssl_cert, -ret, errorbuf); + ssl_cert, (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } @@ -642,7 +643,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading client cert blob: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -662,12 +663,12 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, mbedtls_pk_init(&backend->pk); - if(ssl_config->key || ssl_config->key_blob) { - if(ssl_config->key) { + if(ssl_config->primary.key || ssl_config->primary.key_blob) { + if(ssl_config->primary.key) { #ifdef MBEDTLS_FS_IO #if MBEDTLS_VERSION_NUMBER >= 0x04000000 - ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key, - ssl_config->key_passwd); + ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key, + ssl_config->primary.key_passwd); if(ret == 0 && !(mbedtls_pk_can_do_psa(&backend->pk, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH), @@ -677,8 +678,8 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, 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, + ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key, + ssl_config->primary.key_passwd, mbedtls_ctr_drbg_random, &rng.drbg); if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || @@ -689,7 +690,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading private key %s: (-0x%04X) %s", - ssl_config->key, -ret, errorbuf); + ssl_config->primary.key, (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } #else @@ -698,12 +699,18 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, #endif } else { - const struct curl_blob *ssl_key_blob = ssl_config->key_blob; - const unsigned char *key_data = - (const unsigned char *)ssl_key_blob->data; - const char *passwd = ssl_config->key_passwd; + const struct curl_blob *ssl_key_blob = ssl_config->primary.key_blob; + const char *passwd = ssl_config->primary.key_passwd; + /* Unfortunately, mbedtls_pk_parse_key() requires the data to be + null-terminated if the data is PEM encoded (even when provided the + exact length). */ + unsigned char *newblob = curlx_memdup0(ssl_key_blob->data, + ssl_key_blob->len); + if(!newblob) + return CURLE_OUT_OF_MEMORY; + #if MBEDTLS_VERSION_NUMBER >= 0x04000000 - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, + ret = mbedtls_pk_parse_key(&backend->pk, newblob, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0); if(ret == 0 && @@ -715,7 +722,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, 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, + ret = mbedtls_pk_parse_key(&backend->pk, newblob, ssl_key_blob->len, (const unsigned char *)passwd, passwd ? strlen(passwd) : 0, mbedtls_ctr_drbg_random, @@ -724,11 +731,12 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; #endif + curlx_free(newblob); if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error parsing private key: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } } @@ -738,7 +746,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, } static CURLcode mbed_load_crl(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = @@ -757,7 +765,7 @@ static CURLcode mbed_load_crl(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: error reading CRL file %s: (-0x%04X) %s", - ssl_crlfile, -ret, errorbuf); + ssl_crlfile, (unsigned int)-ret, errorbuf); return CURLE_SSL_CRL_BADFILE; } @@ -791,7 +799,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, char errorbuf[128]; infof(data, "mbedTLS: Connecting to %s:%d", - connssl->peer.hostname, connssl->peer.port); + connssl->peer.origin->hostname, connssl->peer.origin->port); mbedtls_ssl_config_init(&backend->config); ret = mbedtls_ssl_config_defaults(&backend->config, @@ -852,7 +860,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, if(ret) { mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "mbedTLS: ssl_setup failed: " - "(-0x%04X) %s", -ret, errorbuf); + "(-0x%04X) %s", (unsigned int)-ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -903,12 +911,12 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, ret = mbedtls_ssl_session_load(&session, sc_session->sdata, sc_session->sdata_len); if(ret) { - failf(data, "SSL session error loading: -0x%x", -ret); + failf(data, "SSL session error loading: -0x%x", (unsigned int)-ret); } else { ret = mbedtls_ssl_set_session(&backend->ssl, &session); if(ret) - failf(data, "SSL session error setting: -0x%x", -ret); + failf(data, "SSL session error setting: -0x%x", (unsigned int)-ret); else infof(data, "SSL reusing session ID"); } @@ -926,13 +934,14 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, #endif ); - if(ssl_config->key || ssl_config->key_blob) { + if(ssl_config->primary.key || ssl_config->primary.key_blob) { 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)) { + connssl->peer.sni : + connssl->peer.origin->hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and 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. */ @@ -965,7 +974,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, result = (*data->set.ssl.fsslctx)(data, &backend->config, data->set.ssl.fsslctxp); if(result) - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); } return result; @@ -1046,7 +1055,7 @@ static CURLcode mbed_connect_step2(struct Curl_cfilter *cf, mbedtls_ssl_get_version_number(&backend->ssl)); mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "ssl_handshake returned: (-0x%04X) %s", - -ret, errorbuf); + (unsigned int)-ret, errorbuf); return CURLE_SSL_CONNECT_ERROR; } @@ -1158,7 +1167,7 @@ static CURLcode mbed_new_session(struct Curl_cfilter *cf, ret = mbedtls_ssl_get_session(&backend->ssl, &session); msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED); if(ret) { - failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); + failf(data, "mbedtls_ssl_get_session returned -0x%x", (unsigned int)-ret); result = CURLE_SSL_CONNECT_ERROR; goto out; } @@ -1177,7 +1186,7 @@ static CURLcode mbed_new_session(struct Curl_cfilter *cf, ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen); if(ret) { - failf(data, "failed to serialize session: -0x%x", -ret); + failf(data, "failed to serialize session: -0x%x", (unsigned int)-ret); goto out; } @@ -1229,7 +1238,7 @@ 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); + len, (unsigned int)-nwritten); switch(nwritten) { #ifdef MBEDTLS_SSL_PROTO_TLS1_3 case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: @@ -1253,7 +1262,7 @@ static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, } CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d, %zu", - len, result, *pnwritten); + len, (int)result, *pnwritten); return result; } @@ -1297,7 +1306,8 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_SEND; goto out; default: - CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", + (unsigned int)-ret); result = CURLE_RECV_ERROR; goto out; } @@ -1338,7 +1348,8 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_SEND; } else { - CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", -ret); + CURL_TRC_CF(data, cf, "mbedtls_shutdown error -0x%04X", + (unsigned int)-ret); result = CURLE_RECV_ERROR; } @@ -1388,7 +1399,7 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, else { char errorbuf[128]; CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", - buffersize, -nread); + buffersize, (unsigned int)-nread); switch(nread) { #ifdef MBEDTLS_SSL_SESSION_TICKETS case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: @@ -1408,7 +1419,8 @@ static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, break; default: mbedtls_strerror(nread, errorbuf, sizeof(errorbuf)); - failf(data, "ssl_read returned: (-0x%04X) %s", -nread, errorbuf); + failf(data, "ssl_read returned: (-0x%04X) %s", (unsigned int)-nread, + errorbuf); result = CURLE_RECV_ERROR; break; } @@ -1555,7 +1567,7 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input, unsigned char *sha256sum, size_t sha256len) { -#if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 /* mbedTLS 4+ */ +#if defined(PSA_WANT_ALG_SHA_256) && PSA_WANT_ALG_SHA_256 psa_status_t status; size_t sha256len_actual; status = psa_hash_compute(PSA_ALG_SHA_256, input, inputlen, diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 4629ca444435..010bbb9825e4 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -33,7 +33,7 @@ #include "curl_trc.h" #include "httpsrr.h" #include "formdata.h" /* for the boundary function */ -#include "url.h" /* for the ssl config check function */ +#include "url.h" /* for the SSL config check function */ #include "curlx/inet_pton.h" #include "vtls/openssl.h" #include "connect.h" @@ -127,9 +127,9 @@ #endif /* Whether SSL_CTX_set_ciphersuites is available. - * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) * BoringSSL: no * LibreSSL: supported since 3.4.1 (released 2021-10-14) + * OpenSSL: supported since 1.1.1 (commit a53b5be6a05) */ #if (!defined(LIBRESSL_VERSION_NUMBER) || \ (defined(LIBRESSL_VERSION_NUMBER) && \ @@ -142,9 +142,9 @@ #endif /* Whether SSL_CTX_set1_sigalgs_list is available - * OpenSSL: supported since 1.0.2 (commit 0b362de5f575) * BoringSSL: supported since 0.20240913.0 (commit 826ce15) * LibreSSL: no + * OpenSSL: supported since 1.0.2 (commit 0b362de5f575) */ #ifndef LIBRESSL_VERSION_NUMBER #define HAVE_SSL_CTX_SET1_SIGALGS @@ -152,10 +152,10 @@ #ifdef LIBRESSL_VERSION_NUMBER #define OSSL_PACKAGE "LibreSSL" -#elif defined(OPENSSL_IS_BORINGSSL) -#define OSSL_PACKAGE "BoringSSL" #elif defined(OPENSSL_IS_AWSLC) #define OSSL_PACKAGE "AWS-LC" +#elif defined(OPENSSL_IS_BORINGSSL) +#define OSSL_PACKAGE "BoringSSL" #elif defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \ !defined(OPENSSL_QUIC_API2) #define OSSL_PACKAGE "quictls" @@ -415,7 +415,7 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl) if(result) break; - BIO_printf(mem, "%lx", X509_get_version(x)); + BIO_printf(mem, "%lx", (unsigned long)X509_get_version(x)); result = push_certinfo(data, mem, "Version", i); if(result) break; @@ -581,7 +581,7 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) (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); + blen, (int)result, nwritten); BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { @@ -610,7 +610,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) result = Curl_conn_cf_recv(cf->next, data, buf, (size_t)blen, &nread); CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, %zu", - blen, result, nread); + blen, (int)result, nread); BIO_clear_retry_flags(bio); octx->io_result = result; if(result) { @@ -1188,13 +1188,13 @@ static int engineload(struct Curl_easy *data, /* Does the engine supports LOAD_CERT_CTRL ? */ if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME, 0, CURL_UNCONST(cmd_name), NULL)) { - failf(data, "ssl engine does not support loading certificates"); + failf(data, "SSL engine does not support loading certificates"); return 0; } /* 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]", + failf(data, "SSL engine cannot load client cert with id '%s' [%s]", cert_file, ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); @@ -1202,7 +1202,7 @@ static int engineload(struct Curl_easy *data, } 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; } @@ -1671,7 +1671,7 @@ static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name) char buf[256]; ENGINE_free(e); - failf(data, "Failed to initialise SSL Engine '%s': %s", + failf(data, "Failed to initialize SSL Engine '%s': %s", name, ossl_strerror(ERR_get_error(), buf, sizeof(buf))); result = CURLE_SSL_ENGINE_INITFAILED; e = NULL; @@ -2042,19 +2042,19 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ bool iPAddress = FALSE; /* if an iPAddress field exists in the cert */ - size_t hostlen = strlen(peer->hostname); + size_t hostlen = strlen(peer->origin->hostname); (void)conn; switch(peer->type) { case CURL_SSL_PEER_IPV4: - if(!curlx_inet_pton(AF_INET, peer->hostname, &addr)) + if(!curlx_inet_pton(AF_INET, peer->origin->hostname, &addr)) return CURLE_PEER_FAILED_VERIFICATION; target = GEN_IPADD; addrlen = sizeof(struct in_addr); break; #ifdef USE_IPV6 case CURL_SSL_PEER_IPV6: - if(!curlx_inet_pton(AF_INET6, peer->hostname, &addr)) + if(!curlx_inet_pton(AF_INET6, peer->origin->hostname, &addr)) return CURLE_PEER_FAILED_VERIFICATION; target = GEN_IPADD; addrlen = sizeof(struct in6_addr); @@ -2065,7 +2065,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, break; default: DEBUGASSERT(0); - failf(data, "unexpected ssl peer type: %d", peer->type); + failf(data, "unexpected SSL peer type: %d", (int)peer->type); return CURLE_PEER_FAILED_VERIFICATION; } @@ -2115,10 +2115,11 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((altlen == strlen(altptr)) && /* if this is not true, there was an embedded zero in the name string and we cannot match it. */ - Curl_cert_hostcheck(altptr, altlen, peer->hostname, hostlen)) { + Curl_cert_hostcheck(altptr, altlen, + peer->origin->hostname, hostlen)) { matched = TRUE; infof(data, " subjectAltName: \"%s\" matches cert's \"%.*s\"", - peer->dispname, (int)altlen, altptr); + peer->origin->user_hostname, (int)altlen, altptr); } break; @@ -2128,7 +2129,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) { matched = TRUE; infof(data, " subjectAltName: \"%s\" matches cert's IP address!", - peer->dispname); + peer->origin->user_hostname); } break; } @@ -2144,9 +2145,10 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, const char *tname = (peer->type == CURL_SSL_PEER_DNS) ? "hostname" : (peer->type == CURL_SSL_PEER_IPV4) ? "ipv4 address" : "ipv6 address"; - infof(data, " subjectAltName does not match %s %s", tname, peer->dispname); + infof(data, " subjectAltName does not match %s %s", tname, + peer->origin->user_hostname); failf(data, "SSL: no alternative certificate subject name matches " - "target %s '%s'", tname, peer->dispname); + "target %s '%s'", tname, peer->origin->user_hostname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2190,7 +2192,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, if((cnlen <= 0) || !cn) result = CURLE_OUT_OF_MEMORY; else if((size_t)cnlen != strlen((char *)cn)) { - /* there was a terminating zero before the end of string, this + /* there was a null-terminator before the end of string, this cannot match and we return failure! */ failf(data, "SSL: illegal cert name field"); result = CURLE_PEER_FAILED_VERIFICATION; @@ -2206,9 +2208,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data, result = CURLE_PEER_FAILED_VERIFICATION; } else if(!Curl_cert_hostcheck((const char *)cn, cnlen, - peer->hostname, hostlen)) { + peer->origin->hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " - "target hostname '%s'", cn, peer->dispname); + "target hostname '%s'", cn, peer->origin->user_hostname); result = CURLE_PEER_FAILED_VERIFICATION; } else { @@ -2492,7 +2494,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, verstr = "TLSv1.3"; break; default: - curl_msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver); + curl_msnprintf(unknown, sizeof(unknown), "(%x)", (unsigned int)ssl_ver); verstr = unknown; break; } @@ -3010,7 +3012,7 @@ static CURLcode ossl_load_trust_anchors(struct Curl_cfilter *cf, result = load_cacert_from_memory(store, conn_config->ca_info_blob); if(result) { failf(data, "error adding trust anchors from certificate blob: %d", - result); + (int)result); return result; } infof(data, " CA Blob from configuration"); @@ -3322,13 +3324,13 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, return result; } -static CURLcode -ossl_init_session_and_alpns(struct ossl_ctx *octx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const struct alpn_spec *alpns_requested, - Curl_ossl_init_session_reuse_cb *sess_reuse_cb) +static CURLcode ossl_init_session_and_alpns( + struct ossl_ctx *octx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const struct alpn_spec *alpns_requested, + 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); @@ -3371,7 +3373,7 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx, scs->alpn ? scs->alpn : "-"); octx->reused_session = TRUE; infof(data, "SSL verify result: %lx", - SSL_get_verify_result(octx->ssl)); + (unsigned long)SSL_get_verify_result(octx->ssl)); #ifdef HAVE_OPENSSL_EARLYDATA if(ssl_config->earlydata && scs->alpn && SSL_SESSION_get_max_early_data(ssl_session) && @@ -3428,9 +3430,9 @@ 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; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; } @@ -3443,12 +3445,11 @@ 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 = CURLE_OK; if(!CURLECH_ENABLED(data)) return CURLE_OK; - if(data->set.tls_ech & CURLECH_GREASE) { + if(data->set.tls_ech == CURLECH_GREASE) { infof(data, "ECH: will GREASE ClientHello"); #ifdef HAVE_BORINGSSL_LIKE SSL_set_enable_ech_grease(octx->ssl, 1); @@ -3456,10 +3457,11 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); #endif } - else if(data->set.tls_ech & CURLECH_CLA_CFG) { + else if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { #ifdef HAVE_BORINGSSL_LIKE /* have to do base64 decode here for BoringSSL */ const char *b64 = data->set.str[STRING_ECH_CONFIG]; + CURLcode result; if(!b64) { infof(data, "ECH: ECHConfig from command line empty"); @@ -3469,12 +3471,12 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, result = curlx_base64_decode(b64, &ech_config, &ech_config_len); if(result || !ech_config) { infof(data, "ECH: cannot base64 decode ECHConfig from command line"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return result; } 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) { + if(data->set.tls_ech == CURLECH_HARD) { curlx_free(ech_config); return CURLE_SSL_CONNECT_ERROR; } @@ -3490,7 +3492,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]); 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) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else @@ -3500,7 +3502,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } else { const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, peer->origin); if(rinfo && rinfo->echconfiglist) { const unsigned char *ecl = rinfo->echconfiglist; @@ -3509,7 +3511,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, 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) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else { @@ -3519,7 +3521,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } else { infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } } @@ -3531,13 +3533,14 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx, } #else if(trying_ech_now && outername) { + int ret; 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 */); - if(result != 1) { - infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result); + peer->origin->hostname ? peer->origin->hostname : "NULL", outername); + ret = SSL_ech_set1_server_names(octx->ssl, + peer->origin->hostname, outername, + 0 /* do send outer */); + if(ret != 1) { + infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", ret); return CURLE_SSL_CONNECT_ERROR; } } @@ -3675,7 +3678,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, 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; - const char * const ssl_cert_type = ssl_config->cert_type; + const char * const ssl_cert_type = ssl_config->primary.cert_type; unsigned int ssl_version_min; char error_buffer[256]; @@ -3711,16 +3714,19 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return result; } - if(data->set.fdebug && data->set.verbose) { - /* the SSL trace callback is only used for verbose logging */ + if(data->set.fdebug && data->set.verbose && + (peer->transport != TRNSPRT_QUIC)) { + /* the SSL trace callback is only used for verbose logging; + * QUIC connections use a different TLS record format that + * ossl_trace cannot handle */ SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace); SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf); } /* 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 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 + workarounds. The man page for this option states that SSL_OP_ALL enables + all the workarounds 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." @@ -3745,11 +3751,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, CVE-2010-4180 when using previous OpenSSL versions we no longer enable this option regardless of OpenSSL version and SSL_OP_ALL definition. - OpenSSL added a work-around for an SSL 3.0/TLS 1.0 CBC vulnerability: + OpenSSL added a workaround for an SSL 3.0/TLS 1.0 CBC vulnerability: https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt. - In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around + In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that workaround despite the fact that SSL_OP_ALL is documented to do "rather harmless" - workarounds. In order to keep the secure work-around, the + workarounds. In order to keep the secure workaround, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit must not be set. */ @@ -3759,7 +3765,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ctx_options &= ~(ctx_option_t)SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG; /* unless the user explicitly asks to allow the protocol vulnerability we - use the work-around */ + use the workaround */ if(!ssl_config->enable_beast) ctx_options &= ~(ctx_option_t)SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; @@ -3839,8 +3845,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, if(ssl_cert || ssl_cert_blob || ssl_cert_type) { result = client_cert(data, octx->ssl_ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, - ssl_config->key, ssl_config->key_blob, - ssl_config->key_type, ssl_config->key_passwd); + ssl_config->primary.key, ssl_config->primary.key_blob, + ssl_config->primary.key_type, + ssl_config->primary.key_passwd); if(result) /* failf() is already done in client_cert() */ return result; @@ -3946,7 +3953,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, data->set.ssl.fsslctxp); Curl_set_in_callback(data, FALSE); if(result) { - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); return result; } } @@ -4009,6 +4016,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); DEBUGASSERT(octx); + DEBUGASSERT(connssl->peer.origin); result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, connssl->alpn, NULL, NULL, @@ -4217,7 +4225,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } #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 */ + OpenSSL version above v1.1.1, not AWS-LC, BoringSSL, or LibreSSL */ else if((lib == ERR_LIB_SSL) && (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { /* If client certificate is required, communicate the @@ -4263,7 +4271,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, 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); + connssl->peer.origin->hostname, connssl->peer.origin->port); } return result; @@ -4310,7 +4318,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, 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)) { + inner && !strcmp(inner, connssl->peer.origin->hostname)) { VERBOSE(status = "bad name (tolerated without peer verification)"); rv = SSL_ECH_STATUS_SUCCESS; } @@ -4333,7 +4341,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; } @@ -4780,7 +4788,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, ossl_verify = SSL_get_verify_result(octx->ssl); ssl_config->certverifyresult = ossl_verify; - infof(data, "OpenSSL verify result: %lx", ossl_verify); + infof(data, "OpenSSL verify result: %lx", (unsigned long)ossl_verify); verified = (ossl_verify == X509_V_OK); if(verified) @@ -4962,7 +4970,8 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf, if(ssl_connect_1 == connssl->connecting_state) { if(Curl_ossl_need_httpsrr(data) && - !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + !Curl_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); return CURLE_OK; } @@ -5240,7 +5249,7 @@ static CURLcode ossl_recv(struct Curl_cfilter *cf, connssl->input_pending = FALSE; } CURL_TRC_CF(data, cf, "ossl_recv(len=%zu) -> %d, %zu (in_pending=%d)", - buffersize, result, *pnread, connssl->input_pending); + buffersize, (int)result, *pnread, connssl->input_pending); return result; } @@ -5406,6 +5415,9 @@ size_t Curl_ossl_version(char *buffer, size_t size) *p = '_'; } return count; +#elif defined(OPENSSL_IS_AWSLC) + return curl_msnprintf(buffer, size, "%s/%s", + OSSL_PACKAGE, AWSLC_VERSION_NUMBER_STRING); #elif defined(OPENSSL_IS_BORINGSSL) #ifdef CURL_BORINGSSL_VERSION return curl_msnprintf(buffer, size, "%s/%s", @@ -5413,9 +5425,6 @@ size_t Curl_ossl_version(char *buffer, size_t size) #else return curl_msnprintf(buffer, size, OSSL_PACKAGE); #endif -#elif defined(OPENSSL_IS_AWSLC) - 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)); @@ -5440,26 +5449,28 @@ static CURLcode ossl_random(struct Curl_easy *data, return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT; } -static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode ossl_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum /* output */, size_t unused) { + CURLcode result = CURLE_OK; EVP_MD_CTX *mdctx; - unsigned int len = 0; (void)unused; mdctx = EVP_MD_CTX_create(); if(!mdctx) return CURLE_OUT_OF_MEMORY; if(!EVP_DigestInit(mdctx, EVP_sha256())) { - EVP_MD_CTX_destroy(mdctx); - return CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; + goto out; } - EVP_DigestUpdate(mdctx, tmp, tmplen); - EVP_DigestFinal_ex(mdctx, sha256sum, &len); + if(!EVP_DigestUpdate(mdctx, input, len) || + !EVP_DigestFinal_ex(mdctx, sha256sum, NULL)) + result = CURLE_BAD_FUNCTION_ARGUMENT; +out: EVP_MD_CTX_destroy(mdctx); - return CURLE_OK; + return result; } static bool ossl_cert_status_request(void) diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 61d4a1757e25..4fa466b36789 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -33,7 +33,7 @@ * , , or something else, does this: * #define X509_NAME ((LPCSTR)7) * - * In BoringSSL/AWC-LC's there is: + * In AWC-LC/BoringSSL's there is: * typedef struct X509_name_st X509_NAME; * etc. * @@ -74,7 +74,7 @@ #define HAVE_OPENSSL3 /* non-fork OpenSSL 3.x or later */ #endif -#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) +#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL) #define HAVE_BORINGSSL_LIKE #endif @@ -86,9 +86,9 @@ /* * 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. + * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287 */ #ifndef LIBRESSL_VERSION_NUMBER #define HAVE_KEYLOG_CALLBACK @@ -107,12 +107,12 @@ struct Curl_ssl_session; /* Struct to hold a curl OpenSSL instance */ struct ossl_ctx { /* these ones requires specific SSL-types */ - SSL_CTX* ssl_ctx; - SSL* ssl; + SSL_CTX *ssl_ctx; + SSL *ssl; 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; + int blocked_ssl_write_len; #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. */ @@ -183,7 +183,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, /* * Get the server cert, verify it and show it, etc., only call failf() if - * ssl config verifypeer or -host is set. Otherwise all this is for + * SSL config verifypeer or -host is set. Otherwise all this is for * informational purposes only! */ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index d886c8505207..950f17021239 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -45,6 +45,12 @@ #include "curlx/base64.h" #endif +#if EAGAIN != EWOULDBLOCK +#define RAW_EAGAIN(e) ((e) == EWOULDBLOCK || (e) == EAGAIN) +#else +#define RAW_EAGAIN(e) ((e) == EWOULDBLOCK) +#endif + struct rustls_ssl_backend_data { const struct rustls_client_config *config; struct rustls_connection *conn; @@ -118,7 +124,7 @@ static int read_cb(void *userdata, uint8_t *buf, uintptr_t len, 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", - (size_t)len, result, nread); + (size_t)len, (int)result, nread); return ret; } @@ -141,7 +147,7 @@ static int write_cb(void *userdata, const uint8_t *buf, uintptr_t len, } *out_n = (uintptr_t)nwritten; CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %d, %zu", - len, result, nwritten); + len, (int)result, nwritten); return ret; } @@ -160,7 +166,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf, io_ctx.data = data; io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx, &tls_bytes_read); - if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + if(RAW_EAGAIN(io_error)) { *err = CURLE_AGAIN; return -1; } @@ -252,7 +258,7 @@ static CURLcode cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, out: CURL_TRC_CF(data, cf, "rustls_recv(len=%zu) -> %d, %zu", - plainlen, result, *pnread); + plainlen, (int)result, *pnread); return result; } @@ -270,7 +276,7 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, while(rustls_connection_wants_write(rconn)) { io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, &tlswritten); - if(io_error == EAGAIN || io_error == EWOULDBLOCK) { + if(RAW_EAGAIN(io_error)) { CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes", tlswritten_total); return CURLE_AGAIN; @@ -328,7 +334,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(backend->plain_out_buffered) { result = cr_flush_out(cf, data, rconn); CURL_TRC_CF(data, cf, "cf_send: flushing %zu previously added bytes -> %d", - backend->plain_out_buffered, result); + backend->plain_out_buffered, (int)result); if(result) return result; if(blen > backend->plain_out_buffered) { @@ -337,7 +343,7 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, } else blen = 0; - *pnwritten += (ssize_t)backend->plain_out_buffered; + *pnwritten += backend->plain_out_buffered; backend->plain_out_buffered = 0; } @@ -370,11 +376,11 @@ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } else - *pnwritten += (ssize_t)plainwritten; + *pnwritten += plainwritten; out: CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zu", - plainlen, result, *pnwritten); + plainlen, (int)result, *pnwritten); return result; } @@ -845,14 +851,14 @@ init_config_builder_client_auth(struct Curl_easy *data, const struct rustls_certified_key *certified_key = NULL; CURLcode result = CURLE_OK; - if(conn_config->clientcert && !ssl_config->key) { + if(conn_config->clientcert && !ssl_config->primary.key) { failf(data, "rustls: must provide key with certificate '%s'", conn_config->clientcert); return CURLE_SSL_CERTPROBLEM; } - else if(!conn_config->clientcert && ssl_config->key) { + else if(!conn_config->clientcert && ssl_config->primary.key) { failf(data, "rustls: must provide certificate with key '%s'", - ssl_config->key); + ssl_config->primary.key); return CURLE_SSL_CERTPROBLEM; } @@ -866,8 +872,9 @@ init_config_builder_client_auth(struct Curl_easy *data, goto cleanup; } - if(!read_file_into(ssl_config->key, &key_contents)) { - failf(data, "rustls: failed to read key file: '%s'", ssl_config->key); + if(!read_file_into(ssl_config->primary.key, &key_contents)) { + failf(data, "rustls: failed to read key file: '%s'", + ssl_config->primary.key); result = CURLE_SSL_CERTPROBLEM; goto cleanup; } @@ -915,9 +922,9 @@ 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; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; } @@ -957,7 +964,7 @@ init_config_builder_ech(struct Curl_easy *data, return CURLE_OK; } - if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) { + if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { const char *b64 = data->set.str[STRING_ECH_CONFIG]; size_t decode_result; if(!b64) { @@ -974,8 +981,9 @@ init_config_builder_ech(struct Curl_easy *data, } } else { + const struct ssl_connect_data *connssl = cf->ctx; const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, connssl->peer.origin); if(!rinfo || !rinfo->echconfiglist) { failf(data, "rustls: ECH requested but no ECHConfig available"); @@ -997,7 +1005,7 @@ 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]) { + if(data->set.tls_ech && data->set.str[STRING_ECH_CONFIG]) { curlx_free(ech_config); } if(dns) { @@ -1042,6 +1050,12 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, config_builder, cr_verify_none); } else if(ssl_config->native_ca_store) { + if(conn_config->CRLfile) { + failf(data, "rustls: CRL file not supported with native CA store; " + "the platform verifier has no CRL attachment API"); + rustls_client_config_builder_free(config_builder); + return CURLE_NOT_BUILT_IN; + } result = init_config_builder_platform_verifier(data, config_builder); if(result != CURLE_OK) { rustls_client_config_builder_free(config_builder); @@ -1060,7 +1074,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, } } - if(conn_config->clientcert || ssl_config->key) { + if(conn_config->clientcert || ssl_config->primary.key) { result = init_config_builder_client_auth(data, conn_config, ssl_config, @@ -1074,7 +1088,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, #ifdef USE_ECH if(CURLECH_ENABLED(data)) { result = init_config_builder_ech(data, cf, config_builder); - if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) { + if((result != CURLE_OK) && (data->set.tls_ech == CURLECH_HARD)) { rustls_client_config_builder_free(config_builder); return result; } @@ -1093,9 +1107,9 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - DEBUGASSERT(rconn == NULL); + DEBUGASSERT(!rconn); rr = rustls_client_connection_new(backend->config, - connssl->peer.hostname, + connssl->peer.origin->hostname, &rconn); if(rr != RUSTLS_RESULT_OK) { rustls_failf(data, rr, "rustls_client_connection_new"); @@ -1142,14 +1156,15 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(backend); - CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state); + CURL_TRC_CF(data, cf, "cr_connect, state=%d", (int)connssl->state); *done = FALSE; #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_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); return CURLE_OK; } @@ -1159,7 +1174,7 @@ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, result = cr_init_backend(cf, data, (struct rustls_ssl_backend_data *)connssl->backend); - CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); + CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", (int)result); if(result) return result; connssl->state = ssl_connection_negotiating; @@ -1351,7 +1366,7 @@ static CURLcode cr_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, goto out; } DEBUGASSERT(result); - CURL_TRC_CF(data, cf, "shutdown send failed: %d", result); + CURL_TRC_CF(data, cf, "shutdown send failed: %d", (int)result); goto out; } @@ -1368,7 +1383,7 @@ static CURLcode cr_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, } else if(result) { DEBUGASSERT(result); - CURL_TRC_CF(data, cf, "shutdown, error: %d", result); + CURL_TRC_CF(data, cf, "shutdown, error: %d", (int)result); } else if(nread == 0) { /* We got the close notify alert and are done. */ diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index fb5ff0e9d8e6..3e871227aa00 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -69,7 +69,7 @@ #define SCH_DEV_SHOWBOOL(x) do {} while(0) #endif -/* Offered by mingw-w64 v8+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v8+, MS SDK 7.0A/VS2010+ */ #ifndef SP_PROT_TLS1_0_CLIENT #define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT #endif @@ -80,15 +80,16 @@ #define SP_PROT_TLS1_2_CLIENT 0x00000800 #endif -/* Offered by mingw-w64 v8+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v8+, MS SDK 10.0.15063.0/VS2017 15.1+ */ #ifndef SP_PROT_TLS1_3_CLIENT #define SP_PROT_TLS1_3_CLIENT 0x00002000 #endif +/* Offered by mingw-w64 v8+, MS SDK 8.1/~VS2013+ */ #ifndef SCH_USE_STRONG_CRYPTO #define SCH_USE_STRONG_CRYPTO 0x00400000 #endif -/* Offered by mingw-w64 v10+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v10+, MS SDK 7.0A/VS2010+ */ #ifndef SECBUFFER_ALERT #define SECBUFFER_ALERT 17 #endif @@ -105,12 +106,12 @@ * #define failf(x, y, ...) curl_mprintf(y, __VA_ARGS__) */ -/* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v4+, MS SDK 6.0A/VS2008+ */ #ifndef PKCS12_NO_PERSIST_KEY #define PKCS12_NO_PERSIST_KEY 0x00008000 #endif -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_FIND_HAS_PRIVATE_KEY #define CERT_FIND_HAS_PRIVATE_KEY (21 << CERT_COMPARE_SHIFT) #endif @@ -166,7 +167,7 @@ static CURLcode schannel_set_ssl_version_min_max(DWORD *enabled_protocols, VERSION_GREATER_THAN_EQUAL)) { ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; } - else /* Windows 10 and older */ + else /* Windows 10 or older */ ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; break; @@ -191,8 +192,8 @@ static CURLcode schannel_set_ssl_version_min_max(DWORD *enabled_protocols, *enabled_protocols |= SP_PROT_TLS1_3_CLIENT; break; } - else { /* Windows 10 and older */ - failf(data, "schannel: TLS 1.3 not supported on Windows prior to 11"); + else { /* Windows 10 or older */ + failf(data, "schannel: TLS 1.3 not supported on Windows 10 or older"); return CURLE_SSL_CONNECT_ERROR; } } @@ -252,12 +253,12 @@ static const struct algo algs[] = { CIPHEROPTION(CALG_SHA_384), CIPHEROPTION(CALG_SHA_512), CIPHEROPTION(CALG_ECDH), -/* Offered by mingw-w64 v4+. MS SDK 6.0A+. */ +/* Offered by mingw-w64 v4+, MS SDK 6.0A/VS2008+ */ #ifdef CALG_ECMQV CIPHEROPTION(CALG_ECMQV), #endif CIPHEROPTION(CALG_ECDSA), -/* Offered by mingw-w64 v7+. MS SDK 7.0A+. */ +/* Offered by mingw-w64 v7+, MS SDK 7.0A/VS2010+ */ #ifdef CALG_ECDH_EPHEM CIPHEROPTION(CALG_ECDH_EPHEM), #endif @@ -359,15 +360,17 @@ static CURLcode get_cert_location(TCHAR *path, DWORD *store_name, return CURLE_OK; } -static CURLcode get_client_cert(struct Curl_easy *data, +static CURLcode get_client_cert(struct Curl_cfilter *cf, + struct Curl_easy *data, HCERTSTORE *out_cert_store, PCCERT_CONTEXT *out_cert_context) { + struct ssl_primary_config *sslc = Curl_ssl_cf_get_primary_config(cf); PCCERT_CONTEXT client_cert = NULL; HCERTSTORE client_cert_store = NULL; CURLcode result = CURLE_OK; - if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { + if(sslc->clientcert || sslc->cert_blob) { DWORD cert_store_name = 0; TCHAR *cert_store_path = NULL; TCHAR *cert_thumbprint_str = NULL; @@ -378,15 +381,15 @@ static CURLcode get_client_cert(struct Curl_easy *data, FILE *fInCert = NULL; void *certdata = NULL; size_t certsize = 0; - bool blob = data->set.ssl.primary.cert_blob != NULL; + bool blob = !!sslc->cert_blob; if(blob) { - certdata = data->set.ssl.primary.cert_blob->data; - certsize = data->set.ssl.primary.cert_blob->len; + certdata = sslc->cert_blob->data; + certsize = sslc->cert_blob->len; } else { TCHAR *cert_path = - curlx_convert_UTF8_to_tchar(data->set.ssl.primary.clientcert); + curlx_convert_UTF8_to_tchar(sslc->clientcert); if(!cert_path) return CURLE_OUT_OF_MEMORY; @@ -403,22 +406,22 @@ static CURLcode get_client_cert(struct Curl_easy *data, } curlx_free(cert_path); - if(result && (data->set.ssl.primary.clientcert[0] != '\0')) - fInCert = curlx_fopen(data->set.ssl.primary.clientcert, "rb"); + if(result && (sslc->clientcert[0] != '\0')) + fInCert = curlx_fopen(sslc->clientcert, "rb"); if(result && !fInCert) { failf(data, "schannel: Failed to get certificate location" " or file for %s", - data->set.ssl.primary.clientcert); + sslc->clientcert); return result; } } - if((fInCert || blob) && data->set.ssl.cert_type && - !curl_strequal(data->set.ssl.cert_type, "P12")) { + if((fInCert || blob) && sslc->cert_type && + !curl_strequal(sslc->cert_type, "P12")) { failf(data, "schannel: certificate format compatibility error " "for %s", - blob ? "(memory blob)" : data->set.ssl.primary.clientcert); + blob ? "(memory blob)" : sslc->clientcert); curlx_free(cert_store_path); if(fInCert) curlx_fclose(fInCert); @@ -434,7 +437,7 @@ static CURLcode get_client_cert(struct Curl_easy *data, size_t pwd_len = 0; int cert_find_flags; const char *cert_showfilename_error = blob ? - "(memory blob)" : data->set.ssl.primary.clientcert; + "(memory blob)" : sslc->clientcert; curlx_free(cert_store_path); if(fInCert) { long cert_tell = 0; @@ -455,7 +458,7 @@ static CURLcode get_client_cert(struct Curl_easy *data, curlx_fclose(fInCert); if(!continue_reading) { failf(data, "schannel: Failed to read cert file %s", - data->set.ssl.primary.clientcert); + sslc->clientcert); curlx_free(certdata); return CURLE_SSL_CERTPROBLEM; } @@ -465,15 +468,15 @@ static CURLcode get_client_cert(struct Curl_easy *data, datablob.pbData = (BYTE *)certdata; datablob.cbData = (DWORD)certsize; - if(data->set.ssl.key_passwd) - pwd_len = strlen(data->set.ssl.key_passwd); - pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1)); + if(sslc->key_passwd) + pwd_len = strlen(sslc->key_passwd); + pszPassword = 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, - data->set.ssl.key_passwd, + sslc->key_passwd, (int)pwd_len, pszPassword, (int)(pwd_len + 1)); @@ -653,6 +656,11 @@ static CURLcode acquire_sspi_handle(struct Curl_cfilter *cf, if(ciphers) { if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) { + if(!(enabled_protocols & ~SP_PROT_TLS1_3_CLIENT)) { + failf(data, "schannel: TLS 1.3 is not supported with a cipher list; " + "remove the cipher list or allow a lower TLS version"); + return CURLE_SSL_CONNECT_ERROR; + } infof(data, "schannel: WARNING: This version of Schannel " "negotiates a less-secure TLS version than TLS 1.3 because the " "user set an algorithm cipher list."); @@ -789,7 +797,7 @@ static CURLcode schannel_acquire_credential_handle(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - result = get_client_cert(data, &client_cert_store, &client_cert); + result = get_client_cert(cf, data, &client_cert_store, &client_cert); if(result) return result; @@ -843,7 +851,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); #ifdef HAS_ALPN_SCHANNEL backend->use_alpn = connssl->alpn && s_win_has_alpn; @@ -895,7 +903,8 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, /* A hostname associated with the credential is needed by InitializeSecurityContext for SNI and other reasons. */ - snihost = connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname; + snihost = connssl->peer.sni ? + connssl->peer.sni : connssl->peer.origin->hostname; backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); if(!backend->cred->sni_hostname) return CURLE_OUT_OF_MEMORY; @@ -979,7 +988,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, } /* Schannel InitializeSecurityContext: - https://learn.microsoft.com/windows/win32/api/rrascfg/nn-rrascfg-ieapproviderconfig + https://learn.microsoft.com/windows/win32/api/sspi/nf-sspi-initializesecuritycontextw 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 @@ -1238,7 +1247,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_NONE; DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1484,9 +1493,9 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, static bool valid_cert_encoding(const CERT_CONTEXT *cert_context) { - return (cert_context != NULL) && + return cert_context && ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (cert_context->pbCertEncoded != NULL) && + cert_context->pbCertEncoded && (cert_context->cbCertEncoded > 0); } @@ -1579,6 +1588,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, 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; @@ -1590,7 +1600,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)", - connssl->peer.hostname, connssl->peer.port)); + connssl->peer.origin->hostname, connssl->peer.origin->port)); if(!backend->cred) return CURLE_SSL_CONNECT_ERROR; @@ -1656,7 +1666,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf, return result; } - if(data->set.ssl.certinfo) { + if(ssl_config->certinfo) { int certs_count = 0; sspi_status = Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, @@ -1732,7 +1742,7 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf, if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+. MS SDK 7.0A+. */ +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS /* mingw-w64 v9+, MS SDK 7.0A/VS2010+ */ /* When SSPI is used in combination with Schannel * we need the Schannel context to create the Schannel * binding to pass the IIS extended protection checks. @@ -1994,7 +2004,7 @@ static CURLcode 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 *)curlx_malloc(data_len); + ptr = curlx_malloc(data_len); if(!ptr) { return CURLE_OUT_OF_MEMORY; } @@ -2183,7 +2193,7 @@ static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(result == CURLE_AGAIN) SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN")); else { - infof(data, "schannel: recv returned error %d", result); + infof(data, "schannel: recv returned error %d", (int)result); backend->recv_unrecoverable_err = result; } } @@ -2428,7 +2438,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, *done = FALSE; if(backend->ctxt) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %d", - connssl->peer.hostname, connssl->peer.port); + connssl->peer.origin->hostname, connssl->peer.origin->port); } if(!backend->ctxt || cf->shutdown) { @@ -2502,7 +2512,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, else { if(!backend->recv_connection_closed) { result = CURLE_SEND_ERROR; - failf(data, "schannel: error sending close msg: %d", result); + failf(data, "schannel: error sending close msg: %d", (int)result); goto out; } /* Looks like server already closed the connection. @@ -2525,7 +2535,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, connssl->io_need = CURL_SSL_IO_NEED_RECV; } else if(result) { - CURL_TRC_CF(data, cf, "SSL shutdown, error %d", result); + CURL_TRC_CF(data, cf, "SSL shutdown, error %d", (int)result); result = CURLE_RECV_ERROR; } else if(nread == 0) { @@ -2633,16 +2643,24 @@ static CURLcode schannel_random(struct Curl_easy *data, return Curl_win32_random(entropy, length); } -static void schannel_checksum(const unsigned char *input, - size_t inputlen, - unsigned char *checksum, - size_t checksumlen, - DWORD provType, - const unsigned int algId) +static CURLcode schannel_checksum(const unsigned char *input, + size_t inputlen, + unsigned char *checksum, + size_t checksumlen, + DWORD provType, + const unsigned int algId) { + CURLcode result = CURLE_FAILED_INIT; + HCRYPTPROV hProv = 0; HCRYPTHASH hHash = 0; + size_t off; + + DWORD cbHashSize; + DWORD dwHashSizeLen; + DWORD dwChecksumLen; + /* since this can fail in multiple ways, zero memory first so we never * return old data */ @@ -2650,37 +2668,45 @@ static void schannel_checksum(const unsigned char *input, if(!CryptAcquireContext(&hProv, NULL, NULL, provType, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return; /* failed */ + goto out; - do { - DWORD cbHashSize = 0; - DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); - DWORD dwChecksumLen = (DWORD)checksumlen; + if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) + goto out; - if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) - break; /* failed */ + result = CURLE_BAD_FUNCTION_ARGUMENT; - if(!CryptHashData(hHash, input, (DWORD)inputlen, 0)) - break; /* failed */ + off = 0; + while(off < inputlen) { + DWORD chunk = (DWORD)CURLMIN(inputlen - off, 0xffffffffUL); + if(!CryptHashData(hHash, input + off, chunk, 0)) + goto out; + off += chunk; + } - /* get hash size */ - if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, - &dwHashSizeLen, 0)) - break; /* failed */ + /* get hash size */ + cbHashSize = 0; + dwHashSizeLen = (DWORD)sizeof(cbHashSize); + if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, + &dwHashSizeLen, 0)) + goto out; - /* check hash size */ - if(checksumlen < cbHashSize) - break; /* failed */ + /* check if hash fits into the return buffer */ + if(checksumlen < cbHashSize) + goto out; - if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0)) - break; /* failed */ - } while(0); + dwChecksumLen = (DWORD)checksumlen; + if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0) && + dwChecksumLen == cbHashSize) + result = CURLE_OK; +out: if(hHash) CryptDestroyHash(hHash); if(hProv) CryptReleaseContext(hProv, 0); + + return result; } static CURLcode schannel_sha256sum(const unsigned char *input, @@ -2688,9 +2714,8 @@ static CURLcode schannel_sha256sum(const unsigned char *input, unsigned char *sha256sum, size_t sha256len) { - schannel_checksum(input, inputlen, sha256sum, sha256len, - PROV_RSA_AES, CALG_SHA_256); - return CURLE_OK; + return schannel_checksum(input, inputlen, sha256sum, sha256len, + PROV_RSA_AES, CALG_SHA_256); } static void *schannel_get_internals(struct ssl_connect_data *connssl, @@ -2748,10 +2773,11 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, if(share->CAinfo_blob_size != ca_info_blob->len) { return NULL; } - schannel_sha256sum((const unsigned char *)ca_info_blob->data, - ca_info_blob->len, - info_blob_digest, - CURL_SHA256_DIGEST_LENGTH); + if(schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, + info_blob_digest, + CURL_SHA256_DIGEST_LENGTH)) + return NULL; if(memcmp(share->CAinfo_blob_digest, info_blob_digest, CURL_SHA256_DIGEST_LENGTH)) { return NULL; @@ -2789,7 +2815,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, struct Curl_multi *multi = data->multi; const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; struct schannel_cert_share *share; - size_t CAinfo_blob_size = 0; + unsigned char digest[CURL_SHA256_DIGEST_LENGTH]; char *CAfile = NULL; DEBUGASSERT(multi); @@ -2798,12 +2824,26 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, return FALSE; } + if(ca_info_blob) { + if(schannel_sha256sum((const unsigned char *)ca_info_blob->data, + ca_info_blob->len, digest, sizeof(digest))) { + return FALSE; + } + } + else if(conn_config->CAfile) { + CAfile = curlx_strdup(conn_config->CAfile); + if(!CAfile) { + return FALSE; + } + } + share = Curl_hash_pick(&multi->proto_hash, CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY), sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1); if(!share) { share = curlx_calloc(1, sizeof(*share)); if(!share) { + curlx_free(CAfile); return FALSE; } if(!Curl_hash_add2(&multi->proto_hash, @@ -2811,35 +2851,26 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf, sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY) - 1, share, schannel_cert_share_free)) { curlx_free(share); + curlx_free(CAfile); return FALSE; } } - if(ca_info_blob) { - schannel_sha256sum((const unsigned char *)ca_info_blob->data, - ca_info_blob->len, - share->CAinfo_blob_digest, - CURL_SHA256_DIGEST_LENGTH); - CAinfo_blob_size = ca_info_blob->len; - } - else { - if(conn_config->CAfile) { - CAfile = curlx_strdup(conn_config->CAfile); - if(!CAfile) { - return FALSE; - } - } - } - /* free old cache data */ if(share->cert_store) { CertCloseStore(share->cert_store, 0); } curlx_free(share->CAfile); + if(ca_info_blob) { + memcpy(share->CAinfo_blob_digest, digest, sizeof(digest)); + share->CAinfo_blob_size = ca_info_blob->len; + } + else + share->CAinfo_blob_size = 0; + share->time = curlx_now(); share->cert_store = cert_store; - share->CAinfo_blob_size = CAinfo_blob_size; share->CAfile = CAfile; return TRUE; } diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h index 496635f2c2f0..b65ff79926d1 100644 --- a/lib/vtls/schannel_int.h +++ b/lib/vtls/schannel_int.h @@ -42,7 +42,7 @@ #define CERT_STORE_PROV_SYSTEM_W ((LPCSTR)(size_t)10) #endif -/* Offered by mingw-w64 v8+, MS SDK ~10+/~VS2022+ */ +/* Offered by mingw-w64 v8+, MS SDK 10.0.17763.0/VS2017 15.8+ */ #ifndef SCH_CREDENTIALS_VERSION #define SCH_CREDENTIALS_VERSION 0x00000005 @@ -56,42 +56,42 @@ typedef enum _eTlsAlgorithmUsage { /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _CRYPTO_SETTINGS { - eTlsAlgorithmUsage eAlgorithmUsage; - UNICODE_STRING strCngAlgId; - DWORD cChainingModes; - PUNICODE_STRING rgstrChainingModes; /* spellchecker:disable-line */ - DWORD dwMinBitLength; - DWORD dwMaxBitLength; -} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; + eTlsAlgorithmUsage eAlgorithmUsage; + UNICODE_STRING strCngAlgId; + DWORD cChainingModes; + PUNICODE_STRING rgstrChainingModes; /* spellchecker:disable-line */ + DWORD dwMinBitLength; + DWORD dwMaxBitLength; +} CRYPTO_SETTINGS, *PCRYPTO_SETTINGS; /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _TLS_PARAMETERS { - DWORD cAlpnIds; - PUNICODE_STRING rgstrAlpnIds; /* spellchecker:disable-line */ - DWORD grbitDisabledProtocols; - DWORD cDisabledCrypto; - PCRYPTO_SETTINGS pDisabledCrypto; - DWORD dwFlags; -} TLS_PARAMETERS, * PTLS_PARAMETERS; + DWORD cAlpnIds; + PUNICODE_STRING rgstrAlpnIds; /* spellchecker:disable-line */ + DWORD grbitDisabledProtocols; + DWORD cDisabledCrypto; + PCRYPTO_SETTINGS pDisabledCrypto; + DWORD dwFlags; +} TLS_PARAMETERS, *PTLS_PARAMETERS; /* !checksrc! disable TYPEDEFSTRUCT 1 */ typedef struct _SCH_CREDENTIALS { - DWORD dwVersion; - DWORD dwCredFormat; - DWORD cCreds; - PCCERT_CONTEXT* paCred; - HCERTSTORE hRootStore; + DWORD dwVersion; + DWORD dwCredFormat; + DWORD cCreds; + PCCERT_CONTEXT *paCred; + HCERTSTORE hRootStore; - DWORD cMappers; + DWORD cMappers; struct _HMAPPER **aphMappers; - DWORD dwSessionLifespan; - DWORD dwFlags; - DWORD cTlsParameters; - PTLS_PARAMETERS pTlsParameters; -} SCH_CREDENTIALS, * PSCH_CREDENTIALS; + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD cTlsParameters; + PTLS_PARAMETERS pTlsParameters; +} SCH_CREDENTIALS, *PSCH_CREDENTIALS; -#endif /* SCH_CREDENTIALS_VERSION */ +#endif /* !SCH_CREDENTIALS_VERSION */ struct Curl_schannel_cred { CredHandle cred_handle; diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 9be6fe311ccd..e5fe2249a66e 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -71,7 +71,7 @@ struct cert_chain_engine_config_win8 { DWORD dwExclusiveFlags; }; -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG #define CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG 0x00000001 #endif @@ -92,11 +92,6 @@ struct cert_chain_engine_config_win7 { HCERTSTORE hExclusiveTrustedPeople; }; -static int is_cr_or_lf(char c) -{ - return c == '\r' || c == '\n'; -} - /* Search the substring needle,needlelen into string haystack,haystacklen * Strings do not need to be terminated by a '\0'. * Similar of macOS/Linux memmem (not available on Visual Studio). @@ -112,7 +107,7 @@ static const char *c_memmem(const void *haystack, size_t haystacklen, return NULL; first = *(const char *)needle; for(p = (const char *)haystack; p <= (str_limit - needlelen); p++) - if(((*p) == first) && (memcmp(p, needle, needlelen) == 0)) + if((*p == first) && !memcmp(p, needle, needlelen)) return p; return NULL; @@ -134,10 +129,11 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, while(more_certs && (current_ca_file_ptr < ca_buffer_limit)) { const char *begin_cert_ptr = c_memmem(current_ca_file_ptr, - ca_buffer_limit-current_ca_file_ptr, + ca_buffer_limit - + current_ca_file_ptr - 1, BEGIN_CERT, begin_cert_len); - if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[begin_cert_len])) { + if(!begin_cert_ptr || !ISNEWLINE(begin_cert_ptr[begin_cert_len])) { more_certs = 0; } else { @@ -156,7 +152,6 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, CERT_BLOB cert_blob; 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); @@ -166,10 +161,10 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &cert_blob, CERT_QUERY_CONTENT_FLAG_CERT, - CERT_QUERY_FORMAT_FLAG_ALL, + CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED, 0, NULL, - &actual_content_type, + NULL, NULL, NULL, NULL, @@ -186,51 +181,27 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, else { current_ca_file_ptr = begin_cert_ptr + cert_size; - /* Sanity check that the cert_context object is the right type */ - if(CERT_QUERY_CONTENT_CERT != actual_content_type) { + add_cert_result = + CertAddCertificateContextToStore(trust_store, + cert_context, + CERT_STORE_ADD_ALWAYS, + NULL); + if(!add_cert_result) { + char buffer[WINAPI_ERROR_LEN]; failf(data, - "schannel: unexpected content type '%lu' when extracting " - "certificate from CA file '%s'", - actual_content_type, ca_file_text); + "schannel: failed to add certificate from CA file '%s' " + "to certificate store: %s", + ca_file_text, + curlx_winapi_strerror(GetLastError(), buffer, + sizeof(buffer))); result = CURLE_SSL_CACERT_BADFILE; more_certs = 0; } else { - add_cert_result = - CertAddCertificateContextToStore(trust_store, - cert_context, - CERT_STORE_ADD_ALWAYS, - NULL); - if(!add_cert_result) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, - "schannel: failed to add certificate from CA file '%s' " - "to certificate store: %s", - ca_file_text, - curlx_winapi_strerror(GetLastError(), buffer, - sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - num_certs++; - } + 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; - } + CertFreeCertificateContext(cert_context); } } } @@ -254,73 +225,73 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, struct Curl_easy *data) { CURLcode result; - HANDLE ca_file_handle; - LARGE_INTEGER file_size; + FILE *ca_file_handle; char *ca_file_buffer = NULL; - size_t ca_file_bufsize = 0; - DWORD total_bytes_read = 0; + long ca_file_bufsize = 0; + long total_bytes_read = 0; /* * Read the CA file completely into memory before parsing it. This * optimizes for the common case where the CA file is relatively * small ( < 1 MiB ). */ - 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, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + ca_file_handle = curlx_fopen(ca_file, "rb"); + if(!ca_file_handle) { + failf(data, "schannel: failed to open CA file '%s'", ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(!GetFileSizeEx(ca_file_handle, &file_size)) { - char buffer[WINAPI_ERROR_LEN]; - failf(data, "schannel: failed to determine size of CA file '%s': %s", - ca_file, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + if(curlx_fseek(ca_file_handle, 0, SEEK_END)) { + failf(data, "schannel: failed seeking to end of CA file '%s'", ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(file_size.QuadPart > MAX_CAFILE_SIZE) { + ca_file_bufsize = ftell(ca_file_handle); + + if(curlx_fseek(ca_file_handle, 0, SEEK_SET)) { + failf(data, "schannel: failed seeking to beginning of CA file '%s'", + ca_file); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + if(ca_file_bufsize < 0) { + failf(data, "schannel: failed to get length of CA file '%s'", ca_file); + result = CURLE_SSL_CACERT_BADFILE; + goto cleanup; + } + + if(ca_file_bufsize > MAX_CAFILE_SIZE) { failf(data, "schannel: CA file exceeds max size of %d bytes", MAX_CAFILE_SIZE); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - ca_file_bufsize = (size_t)file_size.QuadPart; - ca_file_buffer = (char *)curlx_malloc(ca_file_bufsize + 1); + ca_file_buffer = curlx_malloc(ca_file_bufsize + 1); if(!ca_file_buffer) { result = CURLE_OUT_OF_MEMORY; goto cleanup; } while(total_bytes_read < ca_file_bufsize) { - DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read); - DWORD bytes_read = 0; + size_t nread = fread(ca_file_buffer + total_bytes_read, 1, + ca_file_bufsize - total_bytes_read, ca_file_handle); - 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, - curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); + if(ferror(ca_file_handle)) { + failf(data, "schannel: failed to read from CA file '%s'", ca_file); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; } - if(bytes_read == 0) { + + if(nread == 0) { /* Premature EOF -- adjust the bufsize to the new value */ ca_file_bufsize = total_bytes_read; } else { - total_bytes_read += bytes_read; + total_bytes_read += (long)nread; } } @@ -333,8 +304,8 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, data); cleanup: - if(ca_file_handle != INVALID_HANDLE_VALUE) { - CloseHandle(ca_file_handle); + if(ca_file_handle) { + curlx_fclose(ca_file_handle); } curlx_safefree(ca_file_buffer); @@ -362,7 +333,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data, /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ if(Win8_compat) { -/* Offered by mingw-w64 v4+. MS SDK ~10+/~VS2017+. */ +/* Offered by mingw-w64 v4+, MS SDK 8.0/~VS2012+ */ #ifndef CERT_NAME_SEARCH_ALL_NAMES_FLAG #define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 #endif @@ -382,7 +353,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data, if(!alt_name_info) return 0; - compute_content = host_names != NULL && length != 0; + compute_content = host_names && length != 0; /* Initialize default return values. */ actual_length = 1; @@ -509,7 +480,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) SECURITY_STATUS sspi_status; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; - const char *conn_hostname = connssl->peer.hostname; + const char *conn_hostname = connssl->peer.origin->hostname; size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; @@ -572,7 +543,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) /* CertGetNameString guarantees that the returned name does not contain * embedded null bytes. This appears to be undocumented behavior. */ - cert_hostname_buff = (LPTSTR)curlx_malloc(len * sizeof(TCHAR)); + cert_hostname_buff = curlx_malloc(len * sizeof(TCHAR)); if(!cert_hostname_buff) { result = CURLE_OUT_OF_MEMORY; goto cleanup; @@ -724,15 +695,15 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(ca_info_blob) { result = add_certs_data_to_store(trust_store, - (const char *)ca_info_blob->data, - ca_info_blob->len, - "(memory blob)", - data); + (const char *)ca_info_blob->data, + ca_info_blob->len, + "(memory blob)", + data); } else { result = add_certs_file_to_store(trust_store, - conn_config->CAfile, - data); + conn_config->CAfile, + data); } if(result == CURLE_OK) { if(Curl_schannel_set_cached_cert_store(cf, data, trust_store)) { @@ -780,9 +751,13 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(result == CURLE_OK) { CERT_CHAIN_PARA ChainPara; + LPSTR serverAuthOID = CURL_UNCONST(szOID_PKIX_KP_SERVER_AUTH); memset(&ChainPara, 0, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); + ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; + ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1; + ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = &serverAuthOID; if(!CertGetCertificateChain(cert_chain_engine, pCertContextServer, @@ -805,7 +780,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; - if(data->set.ssl.revoke_best_effort) { + if(ssl_config->revoke_best_effort) { /* Ignore errors when root certificates are missing the revocation * list URL, or when the list could not be downloaded because the * server is currently unreachable. */ diff --git a/lib/vtls/unitytls.c b/lib/vtls/unitytls.c index bbbdec49737e..f28b6ece240b 100644 --- a/lib/vtls/unitytls.c +++ b/lib/vtls/unitytls.c @@ -287,7 +287,7 @@ static unitytls_x509verify_result unitytls_on_verify(void* userData, unitytls_x5 struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); const bool verifypeer = conn_config->verifypeer; const bool verifyhost = conn_config->verifyhost; - const char* const hostname = connssl->peer.hostname; + const char* const hostname = connssl->peer.origin->hostname; unitytls_x509verify_result verify_result = UNITYTLS_X509VERIFY_SUCCESS; /* According to documentation the options verifypeer and verifyhost are independent of each other! */ @@ -404,8 +404,8 @@ static CURLcode unitytls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy 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 struct curl_blob *ssl_key_blob = ssl_config->key_blob; - const char* const hostname = connssl->peer.hostname; + const struct curl_blob *ssl_key_blob = ssl_config->primary.key_blob; + const char* const hostname = connssl->peer.origin->hostname; unitytls_errorstate err = unitytls->unitytls_errorstate_create(); @@ -471,15 +471,15 @@ static CURLcode unitytls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy } /* Load the client private key */ - if(ssl_config->key) { - backend->pk = unitytls_key_parse_pem_from_file(ssl_config->key, ssl_config->key_passwd, &err); + if(ssl_config->primary.key) { + backend->pk = unitytls_key_parse_pem_from_file(ssl_config->primary.key, ssl_config->primary.key_passwd, &err); if(!backend->pk || err.code != UNITYTLS_SUCCESS) { - failf(data, "Error reading private key %s", ssl_config->key); + failf(data, "Error reading private key %s", ssl_config->primary.key); return CURLE_SSL_CERTPROBLEM; } } else if(ssl_key_blob) { - backend->pk = unitytls_key_parse_pem_from_blob(ssl_key_blob, ssl_config->key_passwd, &err); + backend->pk = unitytls_key_parse_pem_from_blob(ssl_key_blob, ssl_config->primary.key_passwd, &err); if(!backend->pk || err.code != UNITYTLS_SUCCESS) { failf(data, "Error parsing private key blob"); return CURLE_SSL_CERTPROBLEM; diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index a3c6892191f4..478727849fd0 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -79,57 +79,6 @@ #include #endif - -#define CLONE_STRING(var) \ - do { \ - if(source->var) { \ - dest->var = curlx_strdup(source->var); \ - if(!dest->var) \ - return FALSE; \ - } \ - else \ - dest->var = NULL; \ - } while(0) - -#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) -{ - DEBUGASSERT(dest); - DEBUGASSERT(!*dest); - if(src) { - /* only if there is data to dupe! */ - struct curl_blob *d; - d = curlx_malloc(sizeof(struct curl_blob) + src->len); - if(!d) - return CURLE_OUT_OF_MEMORY; - d->len = src->len; - /* Always duplicate because the connection may survive longer than the - handle that passed in the blob. */ - d->flags = CURL_BLOB_COPY; - d->data = (void *)((char *)d + sizeof(struct curl_blob)); - memcpy(d->data, src->data, src->len); - *dest = d; - } - return CURLE_OK; -} - -/* returns TRUE if the blobs are identical */ -static bool blobcmp(struct curl_blob *first, struct curl_blob *second) -{ - if(!first && !second) /* both are NULL */ - return TRUE; - if(!first || !second) /* one is NULL */ - return FALSE; - if(first->len != second->len) /* different sizes */ - return FALSE; - return !memcmp(first->data, second->data, first->len); /* same data */ -} - #ifdef USE_SSL #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_PROXY) static const struct alpn_spec ALPN_SPEC_H11 = { @@ -178,265 +127,6 @@ static const struct alpn_spec *alpn_get_spec(http_majors wanted, #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ #endif /* USE_SSL */ -void Curl_ssl_easy_config_init(struct Curl_easy *data) -{ - /* - * libcurl 7.10 introduced SSL verification *by default*! This needs to be - * switched off unless wanted. - */ - data->set.ssl.primary.verifypeer = TRUE; - data->set.ssl.primary.verifyhost = TRUE; - data->set.ssl.primary.cache_session = TRUE; /* caching by default */ -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl = data->set.ssl; -#endif -} - -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) && - (c1->version_max == c2->version_max) && - (c1->ssl_options == c2->ssl_options) && - (c1->verifypeer == c2->verifypeer) && - (c1->verifyhost == c2->verifyhost) && - (c1->verifystatus == c2->verifystatus) && - blobcmp(c1->cert_blob, c2->cert_blob) && - blobcmp(c1->ca_info_blob, c2->ca_info_blob) && - blobcmp(c1->issuercert_blob, c2->issuercert_blob) && - Curl_safecmp(c1->CApath, c2->CApath) && - Curl_safecmp(c1->CAfile, c2->CAfile) && - Curl_safecmp(c1->issuercert, c2->issuercert) && - Curl_safecmp(c1->clientcert, c2->clientcert) && -#ifdef USE_TLS_SRP - !Curl_timestrcmp(c1->username, c2->username) && - !Curl_timestrcmp(c1->password, c2->password) && -#endif - curl_strequal(c1->cipher_list, c2->cipher_list) && - curl_strequal(c1->cipher_list13, c2->cipher_list13) && - curl_strequal(c1->curves, c2->curves) && - curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && - curl_strequal(c1->CRLfile, c2->CRLfile) && - curl_strequal(c1->pinned_key, c2->pinned_key)) - return TRUE; - - return FALSE; -} - -bool Curl_ssl_conn_config_match(struct Curl_easy *data, - struct connectdata *candidate, - bool proxy) -{ -#ifndef CURL_DISABLE_PROXY - if(proxy) - return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, - &candidate->proxy_ssl_config); -#else - (void)proxy; -#endif - return match_ssl_primary_config(data, &data->set.ssl.primary, - &candidate->ssl_config); -} - -static bool clone_ssl_primary_config(struct ssl_primary_config *source, - struct ssl_primary_config *dest) -{ - dest->version = source->version; - dest->version_max = source->version_max; - dest->verifypeer = source->verifypeer; - dest->verifyhost = source->verifyhost; - dest->verifystatus = source->verifystatus; - dest->cache_session = source->cache_session; - dest->ssl_options = source->ssl_options; - - CLONE_BLOB(cert_blob); - CLONE_BLOB(ca_info_blob); - CLONE_BLOB(issuercert_blob); - CLONE_STRING(CApath); - CLONE_STRING(CAfile); - CLONE_STRING(issuercert); - CLONE_STRING(clientcert); - CLONE_STRING(cipher_list); - CLONE_STRING(cipher_list13); - CLONE_STRING(pinned_key); - CLONE_STRING(curves); - CLONE_STRING(signature_algorithms); - CLONE_STRING(CRLfile); -#ifdef USE_TLS_SRP - CLONE_STRING(username); - CLONE_STRING(password); -#endif - - return TRUE; -} - -static void free_primary_ssl_config(struct ssl_primary_config *sslc) -{ - 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 - curlx_safefree(sslc->username); - curlx_safefree(sslc->password); -#endif -} - -CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) -{ - 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 - - 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 - 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 - 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 */ - - return CURLE_OK; -} - -CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, - struct connectdata *conn) -{ - /* Clone "primary" SSL configurations from the easy handle to - * the connection. They are used for connection cache matching and - * probably outlive the easy handle */ - if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) - return CURLE_OUT_OF_MEMORY; -#ifndef CURL_DISABLE_PROXY - if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, - &conn->proxy_ssl_config)) - return CURLE_OUT_OF_MEMORY; -#endif - return CURLE_OK; -} - -void Curl_ssl_conn_config_cleanup(struct connectdata *conn) -{ - free_primary_ssl_config(&conn->ssl_config); -#ifndef CURL_DISABLE_PROXY - free_primary_ssl_config(&conn->proxy_ssl_config); -#endif -} - -void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) -{ - /* May be called on an easy that has no connection yet */ - if(data->conn) { - struct ssl_primary_config *src, *dest; -#ifndef CURL_DISABLE_PROXY - src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary; - dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config; -#else - (void)for_proxy; - src = &data->set.ssl.primary; - dest = &data->conn->ssl_config; -#endif - dest->verifyhost = src->verifyhost; - dest->verifypeer = src->verifypeer; - dest->verifystatus = src->verifystatus; - } -} - #ifdef USE_SSL static int multissl_setup(const struct Curl_ssl *backend); #endif @@ -618,8 +308,7 @@ void Curl_ssl_free_certinfo(struct Curl_easy *data) ci->certinfo[i] = NULL; } - curlx_free(ci->certinfo); /* free the actual array too */ - ci->certinfo = NULL; + curlx_safefree(ci->certinfo); /* free the actual array too */ ci->num_of_certs = 0; } } @@ -1186,12 +875,11 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { + Curl_peer_unlink(&peer->origin); + Curl_peer_unlink(&peer->peer); curlx_safefree(peer->sni); - if(peer->dispname != peer->hostname) - curlx_free(peer->dispname); - peer->dispname = NULL; - curlx_safefree(peer->hostname); curlx_safefree(peer->scache_key); + peer->transport = TRNSPRT_NONE; peer->type = CURL_SSL_PEER_DNS; } @@ -1201,6 +889,8 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(connssl) { connssl->ssl_impl->close(cf, data); connssl->state = ssl_connection_none; + connssl->connecting_state = ssl_connect_1; + connssl->prefs_checked = FALSE; Curl_ssl_peer_cleanup(&connssl->peer); } cf->connected = FALSE; @@ -1225,76 +915,49 @@ static ssl_peer_type get_peer_type(const char *hostname) return CURL_SSL_PEER_DNS; } -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, - struct Curl_cfilter *cf, +CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, uint8_t transport) { - const char *ehostname, *edispname; CURLcode result = CURLE_OUT_OF_MEMORY; /* We expect a clean struct, e.g. called only ONCE */ - DEBUGASSERT(peer); - DEBUGASSERT(!peer->hostname); - DEBUGASSERT(!peer->dispname); - DEBUGASSERT(!peer->sni); - /* We need the hostname for SNI negotiation. Once handshaked, this remains - * the SNI hostname for the TLS connection. When the connection is reused, - * the settings in cf->conn might change. We keep a copy of the hostname we - * use for SNI. - */ - peer->transport = transport; -#ifndef CURL_DISABLE_PROXY - if(Curl_ssl_cf_is_proxy(cf)) { - ehostname = cf->conn->http_proxy.host.name; - edispname = cf->conn->http_proxy.host.dispname; - peer->port = cf->conn->http_proxy.port; - } - else -#endif - { - ehostname = cf->conn->host.name; - edispname = cf->conn->host.dispname; - peer->port = (uint16_t)cf->conn->remote_port; - } - - /* hostname MUST exist and not be empty */ - if(!ehostname || !ehostname[0]) { - result = CURLE_FAILED_INIT; - goto out; + if(!ssl_peer || !origin) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; } + DEBUGASSERT(!ssl_peer->origin); + DEBUGASSERT(!ssl_peer->peer); + DEBUGASSERT(!ssl_peer->sni); + ssl_peer->transport = transport; - peer->hostname = curlx_strdup(ehostname); - if(!peer->hostname) - goto out; - if(!edispname || !strcmp(ehostname, edispname)) - peer->dispname = peer->hostname; - else { - peer->dispname = curlx_strdup(edispname); - if(!peer->dispname) - goto out; - } - peer->type = get_peer_type(peer->hostname); - if(peer->type == CURL_SSL_PEER_DNS) { + Curl_peer_link(&ssl_peer->origin, origin); + Curl_peer_link(&ssl_peer->peer, peer); + ssl_peer->type = get_peer_type(origin->hostname); + if(ssl_peer->type == CURL_SSL_PEER_DNS) { /* 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] == '.')) + size_t len = strlen(origin->hostname); + if(len && (origin->hostname[len - 1] == '.')) len--; if(len < USHRT_MAX) { - peer->sni = curlx_calloc(1, len + 1); - if(!peer->sni) + ssl_peer->sni = curlx_calloc(1, len + 1); + if(!ssl_peer->sni) goto out; - Curl_strntolower(peer->sni, peer->hostname, len); - peer->sni[len] = 0; + Curl_strntolower(ssl_peer->sni, origin->hostname, len); + ssl_peer->sni[len] = 0; } } - result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key); + result = Curl_ssl_peer_key_make(ssl_peer, sslc, tls_id, + &ssl_peer->scache_key); out: if(result) - Curl_ssl_peer_cleanup(peer); + Curl_ssl_peer_cleanup(ssl_peer); return result; } @@ -1309,18 +972,6 @@ static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) cf->ctx = NULL; } -static void ssl_cf_close(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_call_data save; - - CF_DATA_SAVE(save, cf, data); - cf_close(cf, data); - if(cf->next) - cf->next->cft->do_close(cf->next, data); - CF_DATA_RESTORE(cf, save); -} - static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1334,7 +985,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, return CURLE_OK; } - if(!cf->next) { + if(!cf->next || !connssl->peer.origin) { *done = FALSE; return CURLE_FAILED_INIT; } @@ -1359,14 +1010,6 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, connssl->prefs_checked = TRUE; } - if(!connssl->peer.hostname) { - char tls_id[80]; - connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); - result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP); - if(result) - goto out; - } - result = connssl->ssl_impl->do_connect(cf, data, done); if(!result && *done) { @@ -1388,7 +1031,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, connssl->earlydata_state > ssl_earlydata_none); } out: - CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", (int)result, *done); CF_DATA_RESTORE(cf, save); return result; } @@ -1578,7 +1221,7 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data); result = connssl->ssl_impl->shut_down(cf, data, TRUE, done); - CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done); + CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", (int)result, *done); CF_DATA_RESTORE(cf, save); cf->shutdown = (result || *done); } @@ -1682,7 +1325,6 @@ struct Curl_cftype Curl_cft_ssl = { CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, - ssl_cf_close, ssl_cf_shutdown, ssl_cf_adjust_pollset, ssl_cf_data_pending, @@ -1702,7 +1344,6 @@ struct Curl_cftype Curl_cft_ssl_proxy = { CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect, - ssl_cf_close, ssl_cf_shutdown, ssl_cf_adjust_pollset, ssl_cf_data_pending, @@ -1751,28 +1392,53 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, return result; } +static CURLcode cf_ssl_peer_init(struct Curl_cfilter *cf, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc) +{ + struct ssl_connect_data *connssl = cf->ctx; + char tls_id[80]; + connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1); + return Curl_ssl_peer_init(&connssl->peer, origin, peer, sslc, + tls_id, TRNSPRT_TCP); +} + CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct Curl_peer *origin, struct connectdata *conn, int sockindex) { struct Curl_cfilter *cf; + struct Curl_peer *peer = (sockindex == SECONDARYSOCKET) ? + conn->via_peer2 : conn->via_peer; CURLcode result; result = cf_ssl_create(&cf, data, conn); + if(!result) + result = cf_ssl_peer_init(cf, origin, peer, &conn->ssl_config); if(!result) Curl_conn_cf_add(data, conn, sockindex, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer) { struct Curl_cfilter *cf; CURLcode result; result = cf_ssl_create(&cf, data, cf_at->conn); + if(!result) + result = cf_ssl_peer_init(cf, origin, peer, &cf_at->conn->ssl_config); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } @@ -1797,7 +1463,7 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, } #endif - ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, false, use_alpn)); + ctx = cf_ctx_new(data, alpn_get_spec(wanted, 0, FALSE, use_alpn)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1812,14 +1478,19 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf, } CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + struct Curl_peer *peer) { struct Curl_cfilter *cf; CURLcode result; result = cf_ssl_proxy_create(&cf, data, cf_at->conn); + if(!result) + result = cf_ssl_peer_init(cf, peer, NULL, &cf_at->conn->proxy_ssl_config); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else if(cf) + Curl_conn_cf_discard_chain(&cf, data); return result; } @@ -1902,7 +1573,8 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, if(!result && !done) /* blocking failed? */ result = CURLE_SSL_SHUTDOWN_FAILED; Curl_conn_cf_discard(&cf, data); - CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", result); + CURL_TRC_CF(data, cf, "shutdown and remove SSL, done -> %d", + (int)result); break; } } @@ -1914,8 +1586,8 @@ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf) return (cf->cft->flags & CF_TYPE_SSL) && (cf->cft->flags & CF_TYPE_PROXY); } -struct ssl_config_data * -Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) +struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, + struct Curl_easy *data) { #ifdef CURL_DISABLE_PROXY (void)cf; @@ -1925,8 +1597,8 @@ Curl_ssl_cf_get_config(struct Curl_cfilter *cf, struct Curl_easy *data) #endif } -struct ssl_primary_config * -Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf) +struct ssl_primary_config *Curl_ssl_cf_get_primary_config( + struct Curl_cfilter *cf) { #ifdef CURL_DISABLE_PROXY return &cf->conn->ssl_config; diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 6db67cf7482e..96cb7d15ed53 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -49,15 +49,13 @@ struct dynbuf; #define SSLSUPP_ISSUERCERT_BLOB (1 << 14) /* CURLOPT_ISSUERCERT_BLOB */ #ifdef USE_ECH -/* CURLECH_ bits for the tls_ech option */ -#define CURLECH_DISABLE (1 << 0) -#define CURLECH_GREASE (1 << 1) -#define CURLECH_ENABLE (1 << 2) -#define CURLECH_HARD (1 << 3) -#define CURLECH_CLA_CFG (1 << 4) - -#define CURLECH_ENABLED(data) \ - ((data)->set.tls_ech && !((data)->set.tls_ech & CURLECH_DISABLE)) +/* CURLECH_ values for the tls_ech option */ +#define CURLECH_DISABLE 0 +#define CURLECH_GREASE 1 +#define CURLECH_ENABLE 2 +#define CURLECH_HARD 3 + +#define CURLECH_ENABLED(data) ((data)->set.tls_ech) #endif /* USE_ECH */ #define ALPN_ACCEPTED "ALPN: server accepted " @@ -91,12 +89,11 @@ typedef enum { } ssl_peer_type; struct ssl_peer { - char *hostname; /* hostname for verification */ - char *dispname; /* display version of hostname */ + struct Curl_peer *origin; /* the authority we talk to */ + struct Curl_peer *peer; /* the machine we are connected to */ 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 */ - uint16_t port; /* port we are talking to */ uint8_t transport; /* one of TRNSPRT_* defines */ }; @@ -107,48 +104,13 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, curl_sslbackend Curl_ssl_backend(void); -/** - * Init ssl config for a new easy handle. - */ -void Curl_ssl_easy_config_init(struct Curl_easy *data); - -/** - * Init the `data->set.ssl` and `data->set.proxy_ssl` for - * connection matching use. - */ -CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data); - -/** - * Init SSL configs (main + proxy) for a new connection from the easy handle. - */ -CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, - struct connectdata *conn); - -/** - * Free allocated resources in SSL configs (main + proxy) for - * the given connection. - */ -void Curl_ssl_conn_config_cleanup(struct connectdata *conn); - -/** - * Return TRUE iff SSL configuration from `data` is functionally the - * same as the one on `candidate`. - * @param proxy match the proxy SSL config or the main one - */ -bool Curl_ssl_conn_config_match(struct Curl_easy *data, - struct connectdata *candidate, - bool proxy); - -/* Update certain connection SSL config flags after they have - * 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); - /** * Init SSL peer information for filter. Can be called repeatedly. */ -CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, - struct Curl_cfilter *cf, +CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer, + struct Curl_peer *origin, + struct Curl_peer *peer, + struct ssl_primary_config *sslc, const char *tls_id, uint8_t transport); /** @@ -194,7 +156,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, bool Curl_ssl_cert_status_request(void); /* The maximum size of the SSL channel binding is 85 bytes, as defined in - * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long, + * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes, * and SHA-512 is the longest supported hash algorithm, with a digest length of * 64 bytes. * The maximum size of the channel binding is therefore 21 + 64 = 85 bytes. @@ -215,18 +177,22 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data, + struct Curl_peer *origin, struct connectdata *conn, int sockindex); CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *origin, + struct Curl_peer *peer); CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data, int sockindex, bool send_shutdown); #ifndef CURL_DISABLE_PROXY CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + struct Curl_peer *peer); #endif /* !CURL_DISABLE_PROXY */ /** @@ -245,8 +211,8 @@ struct ssl_config_data *Curl_ssl_cf_get_config(struct Curl_cfilter *cf, /** * Get the primary config relevant for the filter from its connection. */ -struct ssl_primary_config * - Curl_ssl_cf_get_primary_config(struct Curl_cfilter *cf); +struct ssl_primary_config *Curl_ssl_cf_get_primary_config( + struct Curl_cfilter *cf); extern struct Curl_cftype Curl_cft_ssl; #ifndef CURL_DISABLE_PROXY @@ -266,7 +232,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #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_add(a, b, c, d) 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 diff --git a/lib/vtls/vtls_config.c b/lib/vtls/vtls_config.c new file mode 100644 index 000000000000..4a9b69654b3c --- /dev/null +++ b/lib/vtls/vtls_config.c @@ -0,0 +1,423 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 HAVE_SYS_TYPES_H +#include +#endif + +#include "urldata.h" +#include "setopt.h" +#include "strcase.h" +#include "vtls/vtls.h" +#include "vtls/vtls_config.h" + + +#define CLONE_STRING(var) \ + do { \ + if(source->var) { \ + dest->var = curlx_strdup(source->var); \ + if(!dest->var) \ + return FALSE; \ + } \ + else \ + dest->var = NULL; \ + } while(0) + +#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) +{ + DEBUGASSERT(dest); + DEBUGASSERT(!*dest); + if(src) { + /* only if there is data to dupe! */ + struct curl_blob *d; + d = curlx_malloc(sizeof(struct curl_blob) + src->len); + if(!d) + return CURLE_OUT_OF_MEMORY; + d->len = src->len; + /* Always duplicate because the connection may survive longer than the + handle that passed in the blob. */ + d->flags = CURL_BLOB_COPY; + d->data = (void *)((char *)d + sizeof(struct curl_blob)); + memcpy(d->data, src->data, src->len); + *dest = d; + } + return CURLE_OK; +} + +/* returns TRUE if the blobs are identical */ +static bool blobcmp(struct curl_blob *first, struct curl_blob *second) +{ + if(!first && !second) /* both are NULL */ + return TRUE; + if(!first || !second) /* one is NULL */ + return FALSE; + if(first->len != second->len) /* different sizes */ + return FALSE; + return !memcmp(first->data, second->data, first->len); /* same data */ +} + +void Curl_ssl_config_init(struct ssl_primary_config *sslc) +{ + /* + * libcurl 7.10 introduced SSL verification *by default*! This needs to be + * switched off unless wanted. + */ + sslc->verifypeer = TRUE; + sslc->verifyhost = TRUE; + sslc->cache_session = TRUE; /* caching by default */ +} + +void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc) +{ + if(sslc->deep_copy) { + 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->key_blob); + curlx_safefree(sslc->curves); + curlx_safefree(sslc->signature_algorithms); + curlx_safefree(sslc->CRLfile); + curlx_safefree(sslc->cert_type); + curlx_safefree(sslc->key); + curlx_safefree(sslc->key_type); + curlx_safefree(sslc->key_passwd); +#ifdef USE_TLS_SRP + curlx_safefree(sslc->username); + curlx_safefree(sslc->password); +#endif + sslc->deep_copy = FALSE; + } +} + +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) && + (c1->version_max == c2->version_max) && + (c1->ssl_options == c2->ssl_options) && + (c1->verifypeer == c2->verifypeer) && + (c1->verifyhost == c2->verifyhost) && + (c1->verifystatus == c2->verifystatus) && + blobcmp(c1->cert_blob, c2->cert_blob) && + blobcmp(c1->ca_info_blob, c2->ca_info_blob) && + blobcmp(c1->issuercert_blob, c2->issuercert_blob) && + blobcmp(c1->key_blob, c2->key_blob) && + Curl_safecmp(c1->CApath, c2->CApath) && + Curl_safecmp(c1->CAfile, c2->CAfile) && + Curl_safecmp(c1->issuercert, c2->issuercert) && + Curl_safecmp(c1->clientcert, c2->clientcert) && +#ifdef USE_TLS_SRP + !Curl_timestrcmp(c1->username, c2->username) && + !Curl_timestrcmp(c1->password, c2->password) && +#endif + curl_strequal(c1->cipher_list, c2->cipher_list) && + curl_strequal(c1->cipher_list13, c2->cipher_list13) && + curl_strequal(c1->curves, c2->curves) && + curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && + Curl_safecmp(c1->CRLfile, c2->CRLfile) && + Curl_safecmp(c1->pinned_key, c2->pinned_key) && + curl_strequal(c1->cert_type, c2->cert_type) && + Curl_safecmp(c1->key, c2->key) && + curl_strequal(c1->key_type, c2->key_type) && + !Curl_timestrcmp(c1->key_passwd, c2->key_passwd)) + return TRUE; + + return FALSE; +} + +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy) +{ +#ifndef CURL_DISABLE_PROXY + if(proxy) + return match_ssl_primary_config(data, &data->set.proxy_ssl.primary, + &candidate->proxy_ssl_config); +#else + (void)proxy; +#endif + return match_ssl_primary_config(data, &data->set.ssl.primary, + &candidate->ssl_config); +} + +static bool clone_ssl_primary_config(struct ssl_primary_config *source, + struct ssl_primary_config *dest) +{ + DEBUGASSERT(!dest->deep_copy); + dest->deep_copy = TRUE; + dest->version = source->version; + dest->version_max = source->version_max; + dest->verifypeer = source->verifypeer; + dest->verifyhost = source->verifyhost; + dest->verifystatus = source->verifystatus; + dest->cache_session = source->cache_session; + dest->ssl_options = source->ssl_options; + + CLONE_BLOB(cert_blob); + CLONE_BLOB(ca_info_blob); + CLONE_BLOB(issuercert_blob); + CLONE_STRING(CApath); + CLONE_STRING(CAfile); + CLONE_STRING(issuercert); + CLONE_STRING(cipher_list); + CLONE_STRING(cipher_list13); + CLONE_STRING(pinned_key); + CLONE_STRING(curves); + CLONE_STRING(signature_algorithms); + CLONE_STRING(CRLfile); + /* SSL credentials: client certificate, SRP auth */ + CLONE_STRING(clientcert); + CLONE_STRING(cert_type); + CLONE_STRING(key); + CLONE_STRING(key_type); + CLONE_STRING(key_passwd); + CLONE_BLOB(key_blob); +#ifdef USE_TLS_SRP + CLONE_STRING(username); + CLONE_STRING(password); +#endif + + return TRUE; +} + +static void ssl_easy_config_compl_options(struct Curl_peer *origin, + struct Curl_peer *initial_origin, + struct ssl_config_data *sslc) +{ + uint8_t options = sslc->primary.ssl_options; + /* If set via CURLOPT_(PROXY_)SSL_OPTIONS, we definitely use it. + * If not, we switch it on for supported backends if no custom + * CA settings exist. */ + sslc->native_ca_store = !!(options & CURLSSLOPT_NATIVE_CA); + sslc->enable_beast = !!(options & CURLSSLOPT_ALLOW_BEAST); + sslc->no_partialchain = !!(options & CURLSSLOPT_NO_PARTIALCHAIN); + sslc->no_revoke = !!(options & CURLSSLOPT_NO_REVOKE); + sslc->revoke_best_effort = !!(options & CURLSSLOPT_REVOKE_BEST_EFFORT); + sslc->earlydata = !!(options & CURLSSLOPT_EARLYDATA); + + sslc->auto_client_cert = Curl_peer_equal(origin, initial_origin) && + !!(options & CURLSSLOPT_AUTO_CLIENT_CERT); +} + +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, + struct Curl_peer *origin) +{ + 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 + + ssl_easy_config_compl_options(origin, data->state.initial_origin, sslc); + + 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.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.ca_info_blob = data->set.blobs[BLOB_CAINFO]; + sslc->primary.curves = data->set.str[STRING_SSL_EC_CURVES]; + /* Maybe these should not be used for another origin. But for + * backwards compatibility, keep them in. */ + sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; + sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT]; + + if(Curl_peer_equal(data->state.initial_origin, origin)) { + sslc->primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; + sslc->primary.cert_blob = data->set.blobs[BLOB_CERT]; + sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE]; + sslc->primary.key = data->set.str[STRING_KEY]; + sslc->primary.key_type = data->set.str[STRING_KEY_TYPE]; + sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD]; + sslc->primary.clientcert = data->set.str[STRING_CERT]; + sslc->primary.key_blob = data->set.blobs[BLOB_KEY]; +#ifdef USE_TLS_SRP + sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME]; + sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD]; +#endif + } + else { + sslc->primary.pinned_key = NULL; + sslc->primary.cert_blob = NULL; + sslc->primary.cert_type = NULL; + sslc->primary.key = NULL; + sslc->primary.key_type = NULL; + sslc->primary.key_passwd = NULL; + sslc->primary.clientcert = NULL; + sslc->primary.key_blob = NULL; +#ifdef USE_TLS_SRP + sslc->primary.username = NULL; + sslc->primary.password = NULL; +#endif + } + +#ifndef CURL_DISABLE_PROXY + sslc = &data->set.proxy_ssl; + /* no initial origin for proxy, it is not changed for redirects */ + ssl_easy_config_compl_options(NULL, NULL, sslc); + + 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->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + sslc->primary.key = data->set.str[STRING_KEY_PROXY]; + sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY]; + sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY]; +#ifdef USE_TLS_SRP + 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 */ + + return CURLE_OK; +} + +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn) +{ + /* Clone "primary" SSL configurations from the easy handle to + * the connection. They are used for connection cache matching and + * probably outlive the easy handle */ + if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) + return CURLE_OUT_OF_MEMORY; +#ifndef CURL_DISABLE_PROXY + if(!clone_ssl_primary_config(&data->set.proxy_ssl.primary, + &conn->proxy_ssl_config)) + return CURLE_OUT_OF_MEMORY; +#endif + return CURLE_OK; +} + +void Curl_ssl_conn_config_cleanup(struct connectdata *conn) +{ + Curl_ssl_config_cleanup(&conn->ssl_config); +#ifndef CURL_DISABLE_PROXY + Curl_ssl_config_cleanup(&conn->proxy_ssl_config); +#endif +} + +void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy) +{ + /* May be called on an easy that has no connection yet */ + if(data->conn) { + struct ssl_primary_config *src, *dest; +#ifndef CURL_DISABLE_PROXY + src = for_proxy ? &data->set.proxy_ssl.primary : &data->set.ssl.primary; + dest = for_proxy ? &data->conn->proxy_ssl_config : &data->conn->ssl_config; +#else + (void)for_proxy; + src = &data->set.ssl.primary; + dest = &data->conn->ssl_config; +#endif + dest->verifyhost = src->verifyhost; + dest->verifypeer = src->verifypeer; + dest->verifystatus = src->verifystatus; + } +} diff --git a/lib/vtls/vtls_config.h b/lib/vtls/vtls_config.h new file mode 100644 index 000000000000..44e691dd267e --- /dev/null +++ b/lib/vtls/vtls_config.h @@ -0,0 +1,125 @@ +#ifndef HEADER_CURL_VTLS_CONFIG_H +#define HEADER_CURL_VTLS_CONFIG_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_peer; + +struct ssl_primary_config { + char *CApath; /* certificate directory (does not work on Windows) */ + char *CAfile; /* certificate to verify peer against */ + char *issuercert; /* optional issuer certificate filename */ + char *clientcert; + char *cipher_list; /* list of ciphers to use */ + char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ + char *signature_algorithms; /* list of signature algorithms to use */ + char *pinned_key; + char *CRLfile; /* CRL to check certificate revocation */ + char *cert_type; /* format for certificate (default: PEM) */ + char *key; /* private key filename */ + char *key_type; /* format for private key (default: PEM) */ + char *key_passwd; /* plain text private key password */ + struct curl_blob *cert_blob; + struct curl_blob *ca_info_blob; + struct curl_blob *issuercert_blob; + struct curl_blob *key_blob; +#ifdef USE_TLS_SRP + char *username; /* TLS username (for, e.g., SRP) */ + char *password; /* TLS password (for, e.g., SRP) */ +#endif + char *curves; /* list of curves 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 */ + BIT(cache_session); /* cache session or not */ + BIT(deep_copy); /* members are deep copies, eg. owned here */ +}; + +struct ssl_config_data { + struct ssl_primary_config primary; + long certverifyresult; /* result from the certificate verification */ + curl_ssl_ctx_callback fsslctx; /* function to initialize SSL ctx */ + void *fsslctxp; /* parameter for call back */ + BIT(certinfo); /* gather lots of certificate info */ + 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(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 { + int ca_cache_timeout; /* Certificate store cache timeout (seconds) */ +}; + +void Curl_ssl_config_init(struct ssl_primary_config *sslc); +void Curl_ssl_config_cleanup(struct ssl_primary_config *sslc); + +/** + * Init the `data->set.ssl` and `data->set.proxy_ssl` for + * connection matching use. + */ +CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data, + struct Curl_peer *origin); + +/** + * Init SSL configs (main + proxy) for a new connection from the easy handle. + */ +CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, + struct connectdata *conn); + +/** + * Free allocated resources in SSL configs (main + proxy) for + * the given connection. + */ +void Curl_ssl_conn_config_cleanup(struct connectdata *conn); + +/** + * Return TRUE iff SSL configuration from `data` is functionally the + * same as the one on `candidate`. + * @param proxy match the proxy SSL config or the main one + */ +bool Curl_ssl_conn_config_match(struct Curl_easy *data, + struct connectdata *candidate, + bool proxy); + +/* Update certain connection SSL config flags after they have + * 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); + +#endif /* HEADER_CURL_VTLS_CONFIG_H */ diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 6700ee74cbe1..fa5da6e4d595 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -38,20 +38,20 @@ 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_0 "http/1.0" #define ALPN_HTTP_1_1_LENGTH 8 -#define ALPN_HTTP_1_1 "http/1.1" -#define ALPN_H2_LENGTH 2 -#define ALPN_H2 "h2" -#define ALPN_H3_LENGTH 2 -#define ALPN_H3 "h3" +#define ALPN_HTTP_1_1 "http/1.1" +#define ALPN_H2_LENGTH 2 +#define ALPN_H2 "h2" +#define ALPN_H3_LENGTH 2 +#define ALPN_H3 "h3" /* conservative sizes on the ALPN entries and count we are handling, * we can increase these if we ever feel the need or have to accommodate * ALPN strings from the "outside". */ -#define ALPN_NAME_MAX 10 -#define ALPN_ENTRIES_MAX 3 -#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) +#define ALPN_NAME_MAX 10 +#define ALPN_ENTRIES_MAX 3 +#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1)) struct alpn_spec { char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX]; @@ -133,9 +133,6 @@ struct ssl_connect_data { 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 - /* Definitions for SSL Implementations */ struct Curl_ssl { @@ -209,3 +206,9 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf, #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */ + +#ifdef USE_SSL +/* Restore the default SSL filter call_data accessor for unity builds. */ +#undef CF_CTX_CALL_DATA +#define CF_CTX_CALL_DATA(cf) ((struct ssl_connect_data *)(cf)->ctx)->call_data +#endif diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 59fa256bc31d..98beeac0368e 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -50,6 +50,7 @@ struct Curl_ssl_scache_peer { char *ssl_peer_key; /* id for peer + relevant TLS configuration */ char *clientcert; + char *key_passwd; char *srp_username; char *srp_password; struct Curl_llist sessions; @@ -85,10 +86,10 @@ static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, if(path[0] != '/') { char *abspath = realpath(path, NULL); if(abspath) { - CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath); + CURLcode result = curlx_dyn_addf(buf, ":%s-%s", name, abspath); /* !checksrc! disable BANNEDFUNC 1 */ free(abspath); /* allocated by libc, free without memdebug */ - return r; + return result; } *is_local = TRUE; } @@ -102,25 +103,67 @@ static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, const char *name, struct curl_blob *blob) { - CURLcode r = CURLE_OK; + CURLcode result = CURLE_OK; if(blob && blob->len) { unsigned char hash[CURL_SHA256_DIGEST_LENGTH]; size_t i; - r = curlx_dyn_addf(buf, ":%s-", name); - if(r) + result = curlx_dyn_addf(buf, ":%s-", name); + if(result) goto out; - r = Curl_sha256it(hash, blob->data, blob->len); - if(r) + result = Curl_sha256it(hash, blob->data, blob->len); + if(result) goto out; for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) { - r = curlx_dyn_addf(buf, "%02x", hash[i]); - if(r) + result = curlx_dyn_addf(buf, "%02x", hash[i]); + if(result) goto out; } } out: - return r; + return result; +} + +static CURLcode cf_ssl_peer_key_add_mtls(struct dynbuf *buf, + struct ssl_primary_config *ssl, + bool *is_local) +{ + CURLcode result = CURLE_OK; + if(ssl->clientcert && ssl->clientcert[0]) { + result = cf_ssl_peer_key_add_path(buf, "CCERT", ssl->clientcert, is_local); + if(result) + goto out; + } + if(ssl->key && ssl->key[0]) { + result = cf_ssl_peer_key_add_path(buf, "KEY", ssl->key, is_local); + if(result) + goto out; + } + if(ssl->key_blob) { + result = cf_ssl_peer_key_add_hash(buf, "KEYBlob", ssl->key_blob); + if(result) + goto out; + } + if(ssl->cert_type && ssl->cert_type[0]) { + size_t i; + result = curlx_dyn_add(buf, ":CT-"); + for(i = 0; !result && ssl->cert_type[i]; i++) { + char c = Curl_raw_toupper(ssl->cert_type[i]); + result = curlx_dyn_addn(buf, &c, 1); + } + if(result) + goto out; + } + if(ssl->key_type && ssl->key_type[0]) { + size_t i; + result = curlx_dyn_add(buf, ":KT-"); + for(i = 0; !result && ssl->key_type[i]; i++) { + char c = Curl_raw_toupper(ssl->key_type[i]); + result = curlx_dyn_addn(buf, &c, 1); + } + } +out: + return result; } #define CURL_SSLS_LOCAL_SUFFIX ":L" @@ -134,156 +177,169 @@ static bool cf_ssl_peer_key_is_global(const char *peer_key) (peer_key[len - 2] == ':'); } -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, - const char *tls_id, - char **ppeer_key) +static CURLcode ssl_peer_key_add_transport(struct dynbuf *buf, + uint8_t transport) { - struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf); - struct dynbuf buf; - size_t key_len; - bool is_local = FALSE; - CURLcode r; - - *ppeer_key = NULL; - curlx_dyn_init(&buf, 10 * 1024); - - r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port); - if(r) - goto out; - - switch(peer->transport) { + switch(transport) { case TRNSPRT_TCP: - break; + return CURLE_OK; case TRNSPRT_UDP: - r = curlx_dyn_add(&buf, ":UDP"); - break; + return curlx_dyn_add(buf, ":UDP"); case TRNSPRT_QUIC: - r = curlx_dyn_add(&buf, ":QUIC"); - break; + return curlx_dyn_add(buf, ":QUIC"); case TRNSPRT_UNIX: - r = curlx_dyn_add(&buf, ":UNIX"); - break; + return curlx_dyn_add(buf, ":UNIX"); default: - r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport); - break; + return curlx_dyn_addf(buf, ":TRNSPRT-%d", transport); } - if(r) - goto out; +} + +static CURLcode ssl_peer_key_add_vrfy(struct dynbuf *buf, + struct ssl_primary_config *ssl, + const struct ssl_peer *peer) +{ + CURLcode result; if(!ssl->verifypeer) { - r = curlx_dyn_add(&buf, ":NO-VRFY-PEER"); - if(r) - goto out; + result = curlx_dyn_add(buf, ":NO-VRFY-PEER"); + if(result) + return result; } if(!ssl->verifyhost) { - r = curlx_dyn_add(&buf, ":NO-VRFY-HOST"); - if(r) - goto out; + result = curlx_dyn_add(buf, ":NO-VRFY-HOST"); + if(result) + return result; } if(ssl->verifystatus) { - r = curlx_dyn_add(&buf, ":VRFY-STATUS"); - if(r) - goto out; + result = curlx_dyn_add(buf, ":VRFY-STATUS"); + if(result) + return result; } - if(!ssl->verifypeer || !ssl->verifyhost) { - if(cf->conn->bits.conn_to_host) { - r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); - if(r) - goto out; - } - if(cf->conn->bits.conn_to_port) { - r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port); - if(r) - goto out; - } + if((!ssl->verifypeer || !ssl->verifyhost) && + peer->peer && !Curl_peer_equal(peer->origin, peer->peer)) { + result = curlx_dyn_addf(buf, ":CHOST-%s:CPORT-%u", + peer->peer->hostname, + peer->peer->port); + if(result) + return result; } + return CURLE_OK; +} +static CURLcode ssl_peer_key_build(struct ssl_primary_config *ssl, + const struct ssl_peer *peer, + const char *tls_id, + char **ppeer_key) +{ + struct dynbuf buf; + size_t key_len; + bool is_local = FALSE; + CURLcode result; + + *ppeer_key = NULL; + curlx_dyn_init(&buf, 10 * 1024); + + result = curlx_dyn_addf(&buf, "%s:%d", + peer->origin->hostname, peer->origin->port); + if(result) + goto out; + result = ssl_peer_key_add_transport(&buf, peer->transport); + if(result) + goto out; + result = ssl_peer_key_add_vrfy(&buf, ssl, peer); + if(result) + goto out; if(ssl->version || ssl->version_max) { - r = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, - (ssl->version_max >> 16)); - if(r) + result = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, + (ssl->version_max >> 16)); + if(result) goto out; } if(ssl->ssl_options) { - r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); - if(r) + result = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options); + if(result) goto out; } if(ssl->cipher_list) { - r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); - if(r) + result = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list); + if(result) goto out; } if(ssl->cipher_list13) { - r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); - if(r) + result = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13); + if(result) goto out; } if(ssl->curves) { - r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); - if(r) + result = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves); + if(result) + goto out; + } + if(ssl->signature_algorithms) { + result = curlx_dyn_addf(&buf, ":SIGALGS-%s", + ssl->signature_algorithms); + if(result) goto out; } if(ssl->verifypeer) { - r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local); + if(result) goto out; - r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local); - if(r) + result = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, + &is_local); + if(result) goto out; if(ssl->ca_info_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); + if(result) goto out; } if(ssl->issuercert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", + ssl->issuercert_blob); + if(result) goto out; } } if(ssl->cert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); - if(r) + result = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); + if(result) goto out; } if(ssl->pinned_key && ssl->pinned_key[0]) { - r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); - if(r) + result = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); + if(result) goto out; } - if(ssl->clientcert && ssl->clientcert[0]) { - r = curlx_dyn_add(&buf, ":CCERT"); - if(r) - goto out; - } + result = cf_ssl_peer_key_add_mtls(&buf, ssl, &is_local); + if(result) + goto out; #ifdef USE_TLS_SRP if(ssl->username || ssl->password) { - r = curlx_dyn_add(&buf, ":SRP-AUTH"); - if(r) + result = curlx_dyn_add(&buf, ":SRP-AUTH"); + if(result) goto out; } #endif if(!tls_id || !tls_id[0]) { - r = CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; goto out; } - r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); - if(r) + result = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id); + if(result) goto out; - r = curlx_dyn_addf(&buf, is_local ? - CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); - if(r) + result = curlx_dyn_addf(&buf, is_local ? + CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX); + if(result) goto out; *ppeer_key = curlx_dyn_take(&buf, &key_len); @@ -292,7 +348,15 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, out: curlx_dyn_free(&buf); - return r; + return result; +} + +CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer, + struct ssl_primary_config *sslc, + const char *tls_id, + char **ppeer_key) +{ + return ssl_peer_key_build(sslc, peer, tls_id, ppeer_key); } struct Curl_ssl_scache { @@ -403,6 +467,7 @@ static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) } peer->sobj_free = NULL; curlx_safefree(peer->clientcert); + curlx_safefree(peer->key_passwd); #ifdef USE_TLS_SRP curlx_safefree(peer->srp_username); curlx_safefree(peer->srp_password); @@ -431,20 +496,20 @@ static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer) * - 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->exportable = (!peer->clientcert && !peer->key_passwd && + !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) +static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, + const char *ssl_peer_key, + const char *clientcert, + const char *key_passwd, + const char *srp_username, + const char *srp_password, + const unsigned char *salt, + const unsigned char *hmac) { CURLcode result = CURLE_OUT_OF_MEMORY; @@ -469,6 +534,11 @@ cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, if(!peer->clientcert) goto out; } + if(key_passwd) { + peer->key_passwd = curlx_strdup(key_passwd); + if(!peer->key_passwd) + goto out; + } if(srp_username) { peer->srp_username = curlx_strdup(srp_username); if(!peer->srp_username) @@ -610,7 +680,7 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, struct ssl_primary_config *conn_config) { if(!conn_config) { - if(peer->clientcert) + if(peer->clientcert || peer->key_passwd) return FALSE; #ifdef USE_TLS_SRP if(peer->srp_username || peer->srp_password) @@ -620,6 +690,8 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, } else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert)) return FALSE; + if(Curl_timestrcmp(peer->key_passwd, conn_config->key_passwd)) + return FALSE; #ifdef USE_TLS_SRP if(Curl_timestrcmp(peer->srp_username, conn_config->username) || Curl_timestrcmp(peer->srp_password, conn_config->password)) @@ -692,8 +764,8 @@ static CURLcode cf_ssl_find_peer_by_key(struct Curl_easy *data, return result; } -static struct Curl_ssl_scache_peer * -cf_ssl_get_free_peer(struct Curl_ssl_scache *scache) +static struct Curl_ssl_scache_peer *cf_ssl_get_free_peer( + struct Curl_ssl_scache *scache) { struct Curl_ssl_scache_peer *peer = NULL; size_t i; @@ -748,6 +820,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, if(peer) { char buffer[64]; const char *ccert = conn_config ? conn_config->clientcert : NULL; + const char *kpasswd = conn_config ? conn_config->key_passwd : NULL; const char *username = NULL, *password = NULL; #ifdef USE_TLS_SRP username = conn_config ? conn_config->username : NULL; @@ -759,7 +832,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, "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, + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, kpasswd, username, password, NULL, NULL); if(result) goto out; @@ -829,7 +902,7 @@ static CURLcode cf_scache_add_session(struct Curl_cfilter *cf, result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer); if(result || !peer) { - CURL_TRC_SSLS(data, "unable to add scache peer: %d", result); + CURL_TRC_SSLS(data, "unable to add scache peer: %d", (int)result); Curl_ssl_session_destroy(s); goto out; } @@ -839,13 +912,13 @@ static CURLcode cf_scache_add_session(struct Curl_cfilter *cf, out: if(result) { failf(data, "[SCACHE] failed to add session for %s, error=%d", - ssl_peer_key, result); + ssl_peer_key, (int)result); } else CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, " "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, " - "quic_tp=%s], peer has %zu sessions now", - ssl_peer_key, s->ietf_tls_id, s->valid_until - now, + "quic_tp=%s], peer has %zu sessions now", ssl_peer_key, + (unsigned int)s->ietf_tls_id, s->valid_until - now, s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no", peer ? Curl_llist_count(&peer->sessions) : 0); return result; @@ -917,7 +990,7 @@ CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf, *ps = s; CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, " "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain", - ssl_peer_key, s->ietf_tls_id, s->alpn, + ssl_peer_key, (unsigned int)s->ietf_tls_id, s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no", Curl_llist_count(&peer->sessions)); } @@ -949,7 +1022,7 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer); if(result || !peer) { - CURL_TRC_SSLS(data, "unable to add scache peer: %d", result); + CURL_TRC_SSLS(data, "unable to add scache peer: %d", (int)result); goto out; } @@ -1040,11 +1113,10 @@ static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer) return result; } -static CURLcode -cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache, - const unsigned char *salt, - const unsigned char *hmac, - struct Curl_ssl_scache_peer **ppeer) +static CURLcode cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache, + const unsigned char *salt, + const unsigned char *hmac, + struct Curl_ssl_scache_peer **ppeer) { size_t i; CURLcode result = CURLE_OK; @@ -1138,7 +1210,7 @@ CURLcode Curl_ssl_session_import(struct Curl_easy *data, if(!peer) { peer = cf_ssl_get_free_peer(scache); if(peer) { - result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, NULL, NULL, NULL, salt, hmac); if(result) goto out; @@ -1172,7 +1244,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, struct Curl_llist_node *n; size_t i; curl_off_t now = time(NULL); - CURLcode r = CURLE_OK; + CURLcode result = CURLE_OK; #ifdef CURLVERBOSE size_t npeers = 0, ntickets = 0; #endif @@ -1188,7 +1260,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, for(i = 0; scache && i < scache->peer_count; i++) { peer = &scache->peers[i]; if(!peer->ssl_peer_key && !peer->hmac_set) - continue; /* skip free entry */ + continue; /* skip free entry */ if(!peer->exportable) continue; @@ -1200,35 +1272,35 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, while(n) { struct Curl_ssl_session *s = Curl_node_elem(n); if(!peer->hmac_set) { - r = cf_ssl_scache_peer_set_hmac(peer); - if(r) + result = cf_ssl_scache_peer_set_hmac(peer); + if(result) goto out; } if(!curlx_dyn_len(&hbuf)) { - r = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt)); - if(r) + result = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt)); + if(result) goto out; - r = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac)); - if(r) + result = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac)); + if(result) goto out; } curlx_dyn_reset(&sbuf); - r = Curl_ssl_session_pack(data, s, &sbuf); - if(r) + result = Curl_ssl_session_pack(data, s, &sbuf); + if(result) goto out; - r = export_fn(data, userptr, peer->ssl_peer_key, - curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf), - curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf), - s->valid_until, s->ietf_tls_id, - s->alpn, s->earlydata_max); - if(r) + result = export_fn(data, userptr, peer->ssl_peer_key, + curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf), + curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf), + s->valid_until, s->ietf_tls_id, + s->alpn, s->earlydata_max); + if(result) goto out; VERBOSE(++ntickets); n = Curl_node_next(n); } } - r = CURLE_OK; + result = CURLE_OK; CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers", ntickets, npeers); @@ -1236,7 +1308,7 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, Curl_ssl_scache_unlock(data); curlx_dyn_free(&hbuf); curlx_dyn_free(&sbuf); - return r; + return result; } #endif /* USE_SSLS_EXPORT */ diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index a6a36f16306b..effb1d8f96af 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -54,20 +54,20 @@ void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache); * connection to the peer. * 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 + * @param sslc the relevant ssl configuration * @param tls_id identifier of TLS implementation for sessions. Should * include full version if session data from other versions * is to be avoided. * @param ppeer_key on successful return, the key generated */ -CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, - const struct ssl_peer *peer, +CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer, + struct ssl_primary_config *sslc, 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 + * 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); diff --git a/lib/vtls/vtls_spack.c b/lib/vtls/vtls_spack.c index d96f4e41bd72..9cbdecc90185 100644 --- a/lib/vtls/vtls_spack.c +++ b/lib/vtls/vtls_spack.c @@ -31,13 +31,6 @@ #include "vtls/vtls_spack.h" #include "curlx/strdup.h" -#ifndef UINT16_MAX -#define UINT16_MAX 0xffff -#endif -#ifndef UINT32_MAX -#define UINT32_MAX 0xffffffff -#endif - #define CURL_SPACK_VERSION 0x01 #define CURL_SPACK_IETF_ID 0x02 #define CURL_SPACK_VALID_UNTIL 0x03 @@ -130,26 +123,26 @@ static CURLcode spack_dec64(uint64_t *val, const uint8_t **src, static CURLcode spack_encstr16(struct dynbuf *buf, const char *s) { size_t slen = strlen(s); - CURLcode r; + CURLcode result; if(slen > UINT16_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc16(buf, (uint16_t)slen); - if(!r) { - r = curlx_dyn_addn(buf, s, slen); + result = spack_enc16(buf, (uint16_t)slen); + if(!result) { + result = curlx_dyn_addn(buf, s, slen); } - return r; + return result; } static CURLcode spack_decstr16(char **val, const uint8_t **src, const uint8_t *end) { uint16_t slen; - CURLcode r; + CURLcode result; *val = NULL; - r = spack_dec16(&slen, src, end); - if(r) - return r; + result = spack_dec16(&slen, src, end); + if(result) + return result; if(end - *src < slen) return CURLE_READ_ERROR; *val = curlx_memdup0((const char *)(*src), slen); @@ -160,26 +153,26 @@ static CURLcode spack_decstr16(char **val, const uint8_t **src, static CURLcode spack_encdata16(struct dynbuf *buf, const uint8_t *data, size_t data_len) { - CURLcode r; + CURLcode result; if(data_len > UINT16_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc16(buf, (uint16_t)data_len); - if(!r) { - r = curlx_dyn_addn(buf, data, data_len); + result = spack_enc16(buf, (uint16_t)data_len); + if(!result) { + result = curlx_dyn_addn(buf, data, data_len); } - return r; + return result; } 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; + CURLcode result; *val = NULL; - r = spack_dec16(&data_len, src, end); - if(r) - return r; + result = spack_dec16(&data_len, src, end); + if(result) + return result; if(end - *src < data_len) return CURLE_READ_ERROR; *val = curlx_memdup0((const char *)(*src), data_len); @@ -192,48 +185,48 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data, struct Curl_ssl_session *s, struct dynbuf *buf) { - CURLcode r; + CURLcode result; DEBUGASSERT(s->sdata); DEBUGASSERT(s->sdata_len); if(s->valid_until < 0) return CURLE_BAD_FUNCTION_ARGUMENT; - r = spack_enc8(buf, CURL_SPACK_VERSION); - if(!r) - r = spack_enc8(buf, CURL_SPACK_TICKET); - if(!r) - r = spack_encdata16(buf, s->sdata, s->sdata_len); - if(!r) - r = spack_enc8(buf, CURL_SPACK_IETF_ID); - if(!r) - r = spack_enc16(buf, (uint16_t)s->ietf_tls_id); - if(!r) - r = spack_enc8(buf, CURL_SPACK_VALID_UNTIL); - if(!r) - r = spack_enc64(buf, (uint64_t)s->valid_until); - if(!r && s->alpn) { - r = spack_enc8(buf, CURL_SPACK_ALPN); - if(!r) - r = spack_encstr16(buf, s->alpn); + result = spack_enc8(buf, CURL_SPACK_VERSION); + if(!result) + result = spack_enc8(buf, CURL_SPACK_TICKET); + if(!result) + result = spack_encdata16(buf, s->sdata, s->sdata_len); + if(!result) + result = spack_enc8(buf, CURL_SPACK_IETF_ID); + if(!result) + result = spack_enc16(buf, (uint16_t)s->ietf_tls_id); + if(!result) + result = spack_enc8(buf, CURL_SPACK_VALID_UNTIL); + if(!result) + result = spack_enc64(buf, (uint64_t)s->valid_until); + if(!result && s->alpn) { + result = spack_enc8(buf, CURL_SPACK_ALPN); + if(!result) + result = spack_encstr16(buf, s->alpn); } - if(!r && s->earlydata_max) { + if(!result && s->earlydata_max) { if(s->earlydata_max > UINT32_MAX) - r = CURLE_BAD_FUNCTION_ARGUMENT; - if(!r) - r = spack_enc8(buf, CURL_SPACK_EARLYDATA); - if(!r) - r = spack_enc32(buf, (uint32_t)s->earlydata_max); + result = CURLE_BAD_FUNCTION_ARGUMENT; + if(!result) + result = spack_enc8(buf, CURL_SPACK_EARLYDATA); + if(!result) + result = spack_enc32(buf, (uint32_t)s->earlydata_max); } - if(!r && s->quic_tp && s->quic_tp_len) { - r = spack_enc8(buf, CURL_SPACK_QUICTP); - if(!r) - r = spack_encdata16(buf, s->quic_tp, s->quic_tp_len); + if(!result && s->quic_tp && s->quic_tp_len) { + result = spack_enc8(buf, CURL_SPACK_QUICTP); + if(!result) + result = spack_encdata16(buf, s->quic_tp, s->quic_tp_len); } - if(r) - CURL_TRC_SSLS(data, "error packing data: %d", r); - return r; + if(result) + CURL_TRC_SSLS(data, "error packing data: %d", (int)result); + return result; } CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, @@ -247,83 +240,83 @@ CURLcode Curl_ssl_session_unpack(struct Curl_easy *data, uint16_t val16; uint32_t val32; uint64_t val64; - CURLcode r; + CURLcode result; DEBUGASSERT(buf); DEBUGASSERT(buflen); *ps = NULL; - r = spack_dec8(&val8, &buf, end); - if(r) + result = spack_dec8(&val8, &buf, end); + if(result) goto out; if(val8 != CURL_SPACK_VERSION) { - r = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; goto out; } s = curlx_calloc(1, sizeof(*s)); if(!s) { - r = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto out; } while(buf < end) { - r = spack_dec8(&val8, &buf, end); - if(r) + result = spack_dec8(&val8, &buf, end); + if(result) goto out; switch(val8) { case CURL_SPACK_ALPN: - r = spack_decstr16(&s->alpn, &buf, end); - if(r) + result = spack_decstr16(&s->alpn, &buf, end); + if(result) goto out; break; case CURL_SPACK_EARLYDATA: - r = spack_dec32(&val32, &buf, end); - if(r) + result = spack_dec32(&val32, &buf, end); + if(result) goto out; s->earlydata_max = val32; break; case CURL_SPACK_IETF_ID: - r = spack_dec16(&val16, &buf, end); - if(r) + result = spack_dec16(&val16, &buf, end); + if(result) goto out; s->ietf_tls_id = val16; break; case CURL_SPACK_QUICTP: { - r = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); - if(r) + result = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end); + if(result) goto out; s->quic_tp = pval8; break; } case CURL_SPACK_TICKET: { - r = spack_decdata16(&pval8, &s->sdata_len, &buf, end); - if(r) + result = spack_decdata16(&pval8, &s->sdata_len, &buf, end); + if(result) goto out; s->sdata = pval8; break; } case CURL_SPACK_VALID_UNTIL: - r = spack_dec64(&val64, &buf, end); - if(r) + result = spack_dec64(&val64, &buf, end); + if(result) goto out; s->valid_until = (curl_off_t)val64; break; default: /* unknown tag */ - r = CURLE_READ_ERROR; + result = CURLE_READ_ERROR; goto out; } } out: - if(r) { - CURL_TRC_SSLS(data, "error unpacking data: %d", r); + if(result) { + CURL_TRC_SSLS(data, "error unpacking data: %d", (int)result); Curl_ssl_session_destroy(s); } else *ps = s; - return r; + return result; } #endif /* USE_SSL && USE_SSLS_EXPORT */ diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 15c81c2874bd..92eaa7a75100 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -81,7 +81,7 @@ options.h. */ #ifndef KEEP_PEER_CERT #if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \ - (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) + (defined(OPENSSL_EXTRA) && !defined(NO_CERTS)) #define KEEP_PEER_CERT #endif #endif @@ -218,26 +218,6 @@ static int wssl_do_file_type(const char *type) return -1; } -#ifdef WOLFSSL_HAVE_KYBER -struct group_name_map { - const word16 group; - const char *name; -}; - -static const struct group_name_map gnm[] = { - { WOLFSSL_ML_KEM_512, "ML_KEM_512" }, - { WOLFSSL_ML_KEM_768, "ML_KEM_768" }, - { WOLFSSL_ML_KEM_1024, "ML_KEM_1024" }, - { WOLFSSL_SECP256R1MLKEM512, "SecP256r1MLKEM512" }, - { WOLFSSL_SECP384R1MLKEM768, "SecP384r1MLKEM768" }, - { WOLFSSL_SECP521R1MLKEM1024, "SecP521r1MLKEM1024" }, - { WOLFSSL_SECP256R1MLKEM768, "SecP256r1MLKEM768" }, - { WOLFSSL_SECP384R1MLKEM1024, "SecP384r1MLKEM1024" }, - { WOLFSSL_X25519MLKEM768, "X25519MLKEM768" }, - { 0, NULL } -}; -#endif - #ifdef USE_BIO_CHAIN static int wssl_bio_cf_create(WOLFSSL_BIO *bio) @@ -316,14 +296,14 @@ static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen) * sending during shutdown. */ CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d" " to %d bytes", blen, wssl->io_send_blocked_len); - skiplen = (ssize_t)(blen - wssl->io_send_blocked_len); + skiplen = (size_t)(blen - wssl->io_send_blocked_len); blen = wssl->io_send_blocked_len; } 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); + blen, (int)result, nwritten); #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif @@ -360,7 +340,7 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) * server response. This allows sending of ClientHello without delay. */ result = Curl_wssl_setup_x509_store(cf, data, wssl); if(result) { - CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result); + CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", (int)result); wssl->io_result = result; return -1; } @@ -368,7 +348,8 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); wssl->io_result = result; - CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %d, %zu", blen, result, nread); + CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %d, %zu", blen, (int)result, + nread); #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif @@ -480,7 +461,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); - DEBUGASSERT(cf != NULL); + DEBUGASSERT(cf); if(cf && session) { struct ssl_connect_data *connssl = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); @@ -517,13 +498,13 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, connssl->earlydata_max); } -static CURLcode -wssl_setup_session(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct wssl_ctx *wss, - struct alpn_spec *alpns, - const char *ssl_peer_key, - Curl_wssl_init_session_reuse_cb *sess_reuse_cb) +static CURLcode wssl_setup_session( + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wssl_ctx *wss, + struct alpn_spec *alpns, + const char *ssl_peer_key, + Curl_wssl_init_session_reuse_cb *sess_reuse_cb) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); struct Curl_ssl_session *scs = NULL; @@ -855,7 +836,7 @@ static CURLcode wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) for(i = 0; (str = wolfSSL_get_cipher_list(i)) != NULL; i++) { size_t n; - if((strncmp(str, "TLS13", 5) == 0) != tls13) + if((!strncmp(str, "TLS13", 5)) != tls13) continue; /* if there already is data in the string, add colon separator */ @@ -920,10 +901,10 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, #ifndef NO_FILESYSTEM if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) { const char *cert_file = ssl_config->primary.clientcert; - const char *key_file = ssl_config->key; + const char *key_file = ssl_config->primary.key; const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; - const struct curl_blob *key_blob = ssl_config->key_blob; - int file_type = wssl_do_file_type(ssl_config->cert_type); + const struct curl_blob *key_blob = ssl_config->primary.key_blob; + int file_type = wssl_do_file_type(ssl_config->primary.cert_type); int rc; switch(file_type) { @@ -954,7 +935,7 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, key_file = cert_file; } else - file_type = wssl_do_file_type(ssl_config->key_type); + file_type = wssl_do_file_type(ssl_config->primary.key_type); rc = key_blob ? wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data, @@ -968,8 +949,8 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, #else /* NO_FILESYSTEM */ if(ssl_config->primary.cert_blob) { const struct curl_blob *cert_blob = ssl_config->primary.cert_blob; - const struct curl_blob *key_blob = ssl_config->key_blob; - int file_type = wssl_do_file_type(ssl_config->cert_type); + const struct curl_blob *key_blob = ssl_config->primary.key_blob; + int file_type = wssl_do_file_type(ssl_config->primary.cert_type); int rc; switch(file_type) { @@ -994,7 +975,7 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, if(!key_blob) key_blob = cert_blob; else - file_type = wssl_do_file_type(ssl_config->key_type); + file_type = wssl_do_file_type(ssl_config->primary.key_type); if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data, (long)key_blob->len, @@ -1076,7 +1057,6 @@ static CURLcode ssl_version(struct Curl_easy *data, return CURLE_OK; } -#define QUIC_GROUPS "P-256:P-384:P-521" #ifdef WOLFSSL_TLS13 #define MAX_CIPHER_LEN 4096 #endif @@ -1137,53 +1117,39 @@ static CURLcode wssl_init_ciphers(struct Curl_easy *data, #endif } +/* wolfSSL_CTX_set1_groups_list() accepts PQC/hybrid groups (e.g. + X25519MLKEM768) that wolfSSL_CTX_set1_curves_list() rejects. It needs + OPENSSL_EXTRA, which wolfSSL's --enable-curl sets but --enable-curl=tiny + does not. */ +#ifdef OPENSSL_EXTRA +#define wssl_CTX_set1_groups_list wolfSSL_CTX_set1_groups_list +#else +#define wssl_CTX_set1_groups_list wolfSSL_CTX_set1_curves_list +#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 - ) + struct ssl_primary_config *conn_config) { 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; - } - } + /* Without an explicit list, leave the key share group selection to + wolfSSL's own default. */ + if(curves && !wssl_CTX_set1_groups_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) +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, + Curl_wssl_init_session_reuse_cb *sess_reuse_cb) { /* Let's make an SSL structure */ wctx->ssl = wolfSSL_new(wctx->ssl_ctx); @@ -1204,15 +1170,6 @@ static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, (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 */ @@ -1260,7 +1217,8 @@ static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG static CURLcode wssl_init_ech(struct wssl_ctx *wctx, struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data, + struct ssl_peer *peer) { int trying_ech_now = 0; @@ -1273,15 +1231,14 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, 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]) { + if(data->set.tls_ech && 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) + if(data->set.tls_ech == CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } else { @@ -1291,7 +1248,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, } else { const struct Curl_https_rrinfo *rinfo = - Curl_conn_dns_get_https(data, cf->sockindex); + Curl_conn_dns_get_https(data, cf->sockindex, peer->origin); if(rinfo && rinfo->echconfiglist) { const unsigned char *ecl = rinfo->echconfiglist; @@ -1301,7 +1258,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != WOLFSSL_SUCCESS) { infof(data, "ECH: wolfSSL_SetEchConfigs failed"); - if(data->set.tls_ech & CURLECH_HARD) { + if(data->set.tls_ech == CURLECH_HARD) { return CURLE_SSL_CONNECT_ERROR; } } @@ -1312,7 +1269,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx, } else { infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) { + if(data->set.tls_ech == CURLECH_HARD) { return CURLE_SSL_CONNECT_ERROR; } } @@ -1341,9 +1298,6 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, struct ssl_primary_config *conn_config; WOLFSSL_METHOD *req_method = NULL; struct alpn_spec alpns; -#ifdef WOLFSSL_HAVE_KYBER - word16 pqkem = 0; -#endif CURLcode result = CURLE_FAILED_INIT; unsigned char transport; int tls_min, tls_max; @@ -1384,11 +1338,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, if(result) goto out; - result = wssl_init_curves(data, wctx, conn_config, transport -#ifdef WOLFSSL_HAVE_KYBER - , &pqkem -#endif - ); + result = wssl_init_curves(data, wctx, conn_config); if(result) goto out; @@ -1441,7 +1391,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, result = (*data->set.ssl.fsslctx)(data, wctx->ssl_ctx, data->set.ssl.fsslctxp); if(result) { - failf(data, "error signaled by ssl ctx callback"); + failf(data, "error signaled by SSL ctx callback"); goto out; } } @@ -1457,17 +1407,13 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, #endif result = wssl_init_ssl_handle(wctx, cf, data, peer, &alpns, ssl_user_data, - transport, -#ifdef WOLFSSL_HAVE_KYBER - pqkem, -#endif - sess_reuse_cb); + transport, sess_reuse_cb); if(result) goto out; #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG if(CURLECH_ENABLED(data)) { - result = wssl_init_ech(wctx, cf, data); + result = wssl_init_ech(wctx, cf, data, peer); if(result) goto out; } @@ -1492,9 +1438,9 @@ 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; + if((data->set.tls_ech == CURLECH_GREASE) || + data->set.str[STRING_ECH_CONFIG]) + return FALSE; return TRUE; #else (void)data; @@ -1722,7 +1668,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) * store to verify the coming certificate from the server */ result = Curl_wssl_setup_x509_store(cf, data, wssl); if(result) { - CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result); + CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", (int)result); return result; } } @@ -1765,9 +1711,9 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "unable to get peer certificate"); return CURLE_PEER_FAILED_VERIFICATION; } - ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.hostname, 0); + ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.origin->hostname, 0); CURL_TRC_CF(data, cf, "check peer certificate for IP match on %s -> %d", - connssl->peer.hostname, ret); + connssl->peer.origin->hostname, ret); if(ret != WOLFSSL_SUCCESS) detail = DOMAIN_NAME_MISMATCH; wolfSSL_X509_free(cert); @@ -1790,7 +1736,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data) * 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); + connssl->peer.origin->hostname); return CURLE_PEER_FAILED_VERIFICATION; } else if(ASN_NO_SIGNER_E == detail) { @@ -1915,7 +1861,7 @@ static CURLcode wssl_send(struct Curl_cfilter *cf, out: CURL_TRC_CF(data, cf, "wssl_send(len=%zu) -> %d, %zu", - blen, result, *pnwritten); + blen, (int)result, *pnwritten); return result; } @@ -2175,7 +2121,8 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, /* 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_conn_dns_resolved_https(data, cf->sockindex, + connssl->peer.peer)) { CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); return CURLE_OK; } @@ -2211,7 +2158,7 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, wssl->hs_result = result; goto out; } - /* handhshake was done without errors */ + /* handshake was done without errors */ #ifdef HAVE_ALPN if(connssl->alpn) { int rc; @@ -2285,8 +2232,8 @@ static CURLcode wssl_random(struct Curl_easy *data, return CURLE_OK; } -static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, +static CURLcode wssl_sha256sum(const unsigned char *input, + size_t len, unsigned char *sha256sum /* output */, size_t unused) { @@ -2294,8 +2241,15 @@ static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */ (void)unused; if(wc_InitSha256(&SHA256pw)) return CURLE_FAILED_INIT; - wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen); - wc_Sha256Final(&SHA256pw, sha256sum); + do { + word32 ilen = (word32)CURLMIN(len, UINT32_MAX); + if(wc_Sha256Update(&SHA256pw, input, ilen)) + return CURLE_BAD_FUNCTION_ARGUMENT; + len -= ilen; + input += ilen; + } while(len); + if(wc_Sha256Final(&SHA256pw, sha256sum)) + return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_OK; } diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c index 788dfb278ade..4c5dac3e8b83 100644 --- a/lib/vtls/x509asn1.c +++ b/lib/vtls/x509asn1.c @@ -1013,7 +1013,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, return 1; /* Compute key length. */ - for(q = elem.beg; !*q && q < elem.end; q++) + for(q = elem.beg; q < elem.end && !*q; q++) ; len = ((elem.end - q) * 8); if(len) { @@ -1062,7 +1062,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, const char *algo, if(p) { if(do_pubkey_field(data, certnum, "dh(p)", &elem)) return 1; - if(getASN1Element(&elem, param->beg, param->end)) { + if(getASN1Element(&elem, p, param->end)) { if(do_pubkey_field(data, certnum, "dh(g)", &elem)) return 1; if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk)) diff --git a/lib/ws.c b/lib/ws.c index d7840f1ffb8d..9820c3e4bd26 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -32,6 +32,7 @@ #include "curlx/dynbuf.h" #include "rand.h" #include "curlx/base64.h" +#include "cf-recvbuf.h" #include "connect.h" #include "sendf.h" #include "curl_trc.h" @@ -632,6 +633,7 @@ static CURLcode ws_enc_add_cntrl(struct Curl_easy *data, size_t plen, unsigned int frame_type) { + (void)data; DEBUGASSERT(plen <= WS_MAX_CNTRL_LEN); if(plen > WS_MAX_CNTRL_LEN) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -641,13 +643,6 @@ static CURLcode ws_enc_add_cntrl(struct Curl_easy *data, ws->pending.type = frame_type; ws->pending.payload_len = plen; memcpy(ws->pending.payload, payload, plen); - - if(!ws->enc.payload_remain) { /* not in the middle of another frame */ - CURLcode result = ws_enc_add_pending(data, ws); - if(!result) - (void)ws_flush(data, ws, Curl_is_in_callback(data)); - return result; - } return CURLE_OK; } @@ -716,7 +711,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, { struct ws_cw_ctx *ctx = writer->ctx; struct websocket *ws; - CURLcode result; + CURLcode result = CURLE_OK; CURL_TRC_WRITE(data, "ws_cw_write(len=%zu, type=%d)", nbytes, type); if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode) @@ -733,7 +728,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, result = Curl_bufq_write(&ctx->buf, (const uint8_t *)buf, nbytes, &nwritten); if(result) { - infof(data, "[WS] error adding data to buffer %d", result); + infof(data, "[WS] error adding data to buffer %d", (int)result); return result; } } @@ -749,7 +744,8 @@ static CURLcode ws_cw_write(struct Curl_easy *data, if(result == CURLE_AGAIN) { /* insufficient amount of data, keep it for later. * we pretend to have written all since we have a copy */ - return CURLE_OK; + result = CURLE_OK; + goto out; } else if(result) { failf(data, "[WS] decode payload error %d", (int)result); @@ -760,10 +756,16 @@ static CURLcode ws_cw_write(struct Curl_easy *data, if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { failf(data, "[WS] decode ending with %zu frame bytes remaining", Curl_bufq_len(&ctx->buf)); - return CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; } - return CURLE_OK; +out: + if(!result) { + result = ws_flush(data, ws, Curl_is_in_callback(data)); + if(result == CURLE_AGAIN) + result = CURLE_OK; + } + return result; } /* WebSocket payload decoding client writer. */ @@ -995,7 +997,7 @@ static CURLcode ws_enc_add_pending(struct Curl_easy *data, &ws->sendbuf); if(result) { CURL_TRC_WS(data, "ws_enc_cntrl(), error adding head: %d", - result); + (int)result); goto out; } result = ws_enc_write_payload(&ws->enc, data, ws->pending.payload, @@ -1003,7 +1005,7 @@ static CURLcode ws_enc_add_pending(struct Curl_easy *data, &ws->sendbuf, &n); if(result) { CURL_TRC_WS(data, "ws_enc_cntrl(), error adding payload: %d", - result); + (int)result); goto out; } if(n != ws->pending.payload_len) { @@ -1065,7 +1067,8 @@ static CURLcode ws_enc_send(struct Curl_easy *data, fragsize : (curl_off_t)buflen, &ws->sendbuf); if(result) { - CURL_TRC_WS(data, "curl_ws_send(), error writing frame head %d", result); + CURL_TRC_WS(data, "curl_ws_send(), error writing frame head %d", + (int)result); return result; } } @@ -1219,7 +1222,7 @@ static CURLcode cr_ws_read(struct Curl_easy *data, out: CURL_TRC_READ(data, "cr_ws_read(len=%zu) -> %d, nread=%zu, eos=%d", - blen, result, *pnread, *peos); + blen, (int)result, *pnread, *peos); return result; } @@ -1390,15 +1393,17 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, 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 uint8_t *)mem, - nread, &nwritten); - if(result) - goto out; - DEBUGASSERT(nread == nwritten); + * when using `curl_ws_recv/curl_easy_recv` later on, after this transfer + * is already marked as DONE. + * Since `curl_easy_recv()` is also supposed to work, we need + * to buffer the data at connection level. See #22107 */ + if(nread) { + result = Curl_cf_recvbuf_add(data, data->conn, FIRSTSOCKET, + (const uint8_t *)mem, nread); + if(result) + goto out; + } CURL_REQ_CLEAR_RECV(data); /* read no more content */ } else { /* !connect_only */ @@ -1443,7 +1448,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, if(ws_enc_reader) Curl_creader_free(data, ws_enc_reader); if(result) - CURL_TRC_WS(data, "Curl_ws_accept() failed -> %d", result); + CURL_TRC_WS(data, "Curl_ws_accept() failed -> %d", (int)result); else CURL_TRC_WS(data, "websocket established, %s mode", data->set.connect_only ? "connect-only" : "callback"); @@ -1627,8 +1632,16 @@ CURLcode curl_ws_recv(CURL *curl, void *buffer, static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, bool blocking) { + CURLcode result; + + /* If there is space, add any pending control frame */ + if(Curl_bufq_len(&ws->sendbuf) < ws->sendbuf.chunk_size) { + result = ws_enc_add_pending(data, ws); + if(result && (result != CURLE_AGAIN)) + return result; + } + if(!Curl_bufq_is_empty(&ws->sendbuf)) { - CURLcode result; const uint8_t *out; size_t outlen, n; #ifdef DEBUGBUILD @@ -1670,7 +1683,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, return result; } else if(result) { - failf(data, "[WS] flush, write error %d", result); + failf(data, "[WS] flush, write error %d", (int)result); return result; } else { @@ -1761,7 +1774,7 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, } CURL_TRC_WS(data, "ws_send_raw(len=%zu) -> %d, %zu", - buflen, result, *pnwritten); + buflen, (int)result, *pnwritten); return result; } @@ -1837,7 +1850,7 @@ CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, out: CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T ", flags=%x, raw=%d) -> %d, %zu", - buflen, fragsize, flags, data->set.ws_raw_mode, result, + buflen, fragsize, flags, data->set.ws_raw_mode, (int)result, *pnsent); return result; } @@ -1909,7 +1922,7 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, &ws->sendbuf); if(result) CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", - result); + (int)result); out: return result; diff --git a/m4/curl-amissl.m4 b/m4/curl-amissl.m4 index da90cc412d7a..e32861e9ef12 100644 --- a/m4/curl-amissl.m4 +++ b/m4/curl-amissl.m4 @@ -45,7 +45,7 @@ if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then test "amissl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes AMISSL_ENABLED=1 OPENSSL_ENABLED=1 - dnl 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" @@ -63,5 +63,4 @@ if test "$HAVE_PROTO_BSDSOCKET_H" = "1"; then else AC_MSG_RESULT(no) fi - ]) diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 index 4bb7196eae13..8b7da20caaba 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 @@ -64,11 +64,11 @@ AC_DEFUN([CURL_CHECK_COMPILER], [ *** compiler you are using, relative to the flags required to enable or *** disable generation of debug info, optimization options or warnings. *** -*** Whatever settings are present in CFLAGS will be used for this run. +*** Whatever settings are present in CFLAGS are used for this run. *** *** If you wish to help the curl project to better support your compiler *** you can report this and the required info on the libcurl development -*** mailing list: https://lists.haxx.selistinfo/curl-library/ +*** mailing list: https://lists.haxx.se/listinfo/curl-library/ *** _EOF fi @@ -173,8 +173,8 @@ dnl CURL_CHECK_COMPILER_GNU_C dnl ------------------------------------------------- dnl Verify if compiler being used is GNU C dnl -dnl $compiler_num will be set to MAJOR * 100 + MINOR for gcc less than version -dnl 7 and just $MAJOR * 100 for gcc version 7 and later. +dnl $compiler_num is set to MAJOR * 100 + MINOR for gcc less than version +dnl 7 and $MAJOR * 100 for gcc version 7 and later. dnl dnl Examples: dnl Version 1.2.3 => 102 @@ -526,8 +526,8 @@ AC_DEFUN([CURL_SET_COMPILER_BASIC_OPTS], [ CLANG|APPLECLANG) - dnl Disable warnings for unused arguments, otherwise clang will - dnl warn about compile-time arguments used during link-time, like + dnl Disable warnings for unused arguments, otherwise clang warns + dnl 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" @@ -713,7 +713,7 @@ AC_DEFUN([CURL_SET_COMPILER_OPTIMIZE_OPTS], [ 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 This initially assumed optimizer setting are finally ignored dnl if CFLAGS or CPPFLAGS already hold optimizer flags. This implies dnl that an initially assumed optimizer setting might not be honored. @@ -862,6 +862,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [conditional-uninitialized]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [language-extension-token]) fi + dnl Only clang 3.1 or later if test "$compiler_num" -ge "301"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [format-non-iso]) @@ -883,6 +884,7 @@ 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" @@ -914,38 +916,46 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ tmp_CFLAGS="$tmp_CFLAGS -Wno-varargs" fi fi + dnl clang 7 or later if test "$compiler_num" -ge "700"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [assign-enum]) CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [extra-semi-stmt]) fi + dnl clang 10 or later if test "$compiler_num" -ge "1000"; then 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" + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [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]) @@ -966,7 +976,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ 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 + tmp_CFLAGS="$tmp_CFLAGS -Wno-comma" # Silly fi ;; esac @@ -1105,6 +1115,11 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ tmp_CFLAGS="$tmp_CFLAGS -ftree-vrp" fi + dnl Only gcc 4.4 or later + if test "$compiler_num" -ge "404"; then + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [logical-op]) + fi + dnl Only gcc 4.5 or later if test "$compiler_num" -ge "405"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [jump-misses-init]) @@ -1134,7 +1149,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ 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" + CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [format-signedness]) fi dnl Only gcc 6 or later @@ -1245,7 +1260,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ tmp_CPPFLAGS="$tmp_CPPFLAGS -Wp64" dnl Enable warnings for questionable pointer arithmetic tmp_CPPFLAGS="$tmp_CPPFLAGS -Wpointer-arith" - dnl Check for function return typw issues + dnl Check for function return type issues tmp_CPPFLAGS="$tmp_CPPFLAGS -Wreturn-type" dnl Warn on variable declarations hiding a previous one tmp_CPPFLAGS="$tmp_CPPFLAGS -Wshadow" diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4 index fe20155564a0..d3e1a33b6bb5 100644 --- a/m4/curl-confopts.m4 +++ b/m4/curl-confopts.m4 @@ -150,25 +150,25 @@ AS_HELP_STRING([--disable-optimize],[Disable compiler optimizations]), OPT_COMPILER_OPTIMIZE=$enableval) case "$OPT_COMPILER_OPTIMIZE" in no) - dnl --disable-optimize option used. We will handle this as - dnl a request to disable compiler optimizations if possible. - dnl If the compiler is known CFLAGS and CPPFLAGS will be - dnl overridden, otherwise this can not be honored. + dnl --disable-optimize option used. We handle this as a request + dnl to disable compiler optimizations if possible. If the compiler + dnl is known CFLAGS and CPPFLAGS are overridden, otherwise this + dnl can not be honored. want_optimize="no" AC_MSG_RESULT([no]) ;; default) - dnl configure's optimize option not specified. Initially we will - dnl handle this as a request contrary to configure's setting - dnl for --enable-debug. IOW, initially, for debug-enabled builds - dnl this will be handled as a request to disable optimizations if - dnl possible, and for debug-disabled builds this will be handled - dnl initially as a request to enable optimizations if possible. - dnl Finally, if the compiler is known and CFLAGS and CPPFLAGS do - dnl not have any optimizer flag the request will be honored, in - dnl any other case the request can not be honored. + dnl configure's optimize option not specified. Initially we handle + dnl this as a request contrary to configure's setting for + dnl --enable-debug. IOW, initially, for debug-enabled builds this + dnl is handled as a request to disable optimizations if possible, + dnl and for debug-disabled builds this is handled initially as + dnl a request to enable optimizations if possible. Finally, if the + dnl compiler is known and CFLAGS and CPPFLAGS do not have any + dnl optimizer flag the request is honored, in any other case the + dnl request can not be honored. dnl IOW, existing optimizer flags defined in CFLAGS or CPPFLAGS - dnl will always take precedence over any initial assumption. + dnl always take precedence over any initial assumption. if test "$want_debug" = "yes"; then want_optimize="assume_no" AC_MSG_RESULT([(assumed) no]) @@ -178,10 +178,10 @@ AS_HELP_STRING([--disable-optimize],[Disable compiler optimizations]), fi ;; *) - dnl --enable-optimize option used. We will handle this as - dnl a request to enable compiler optimizations if possible. - dnl If the compiler is known CFLAGS and CPPFLAGS will be - dnl overridden, otherwise this can not be honored. + dnl --enable-optimize option used. We handle this as a request + dnl to enable compiler optimizations if possible. If the compiler + dnl is known CFLAGS and CPPFLAGS are overridden, otherwise this + dnl can not be honored. want_optimize="yes" AC_MSG_RESULT([yes]) ;; @@ -207,7 +207,7 @@ AS_HELP_STRING([--disable-symbol-hiding],[Disable hiding of library internal sym no) dnl --disable-symbol-hiding option used. dnl This is an indication to not attempt hiding of library internal - dnl symbols. Default symbol visibility will be used, which normally + dnl symbols. Default symbol visibility is used, which normally dnl exposes all library internal symbols. want_symbol_hiding="no" AC_MSG_RESULT([no]) @@ -361,13 +361,13 @@ dnl CURL_CONFIGURE_SYMBOL_HIDING dnl ------------------------------------------------- dnl Depending on --enable-symbol-hiding or --disable-symbol-hiding dnl configure option, and compiler capability to actually honor such -dnl option, this will modify compiler flags as appropriate and also -dnl provide needed definitions for configuration and Makefile.am files. +dnl option, this modifies compiler flags as appropriate and also +dnl provides needed definitions for configuration and Makefile.am files. dnl This macro should not be used until all compilation tests have dnl been done to prevent interferences on other tests. AC_DEFUN([CURL_CONFIGURE_SYMBOL_HIDING], [ - AC_MSG_CHECKING([whether hiding of library internal symbols will actually happen]) + AC_MSG_CHECKING([whether hiding of library internal symbols does actually happen]) CFLAG_CURL_SYMBOL_HIDING="" doing_symbol_hiding="no" if test "$want_symbol_hiding" = "yes" && @@ -456,7 +456,7 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [ ]) if test "$want_ares" = "yes"; then - dnl finally c-ares will be used + dnl finally c-ares is used AC_DEFINE(USE_ARES, 1, [Define to enable c-ares support]) USE_ARES=1 LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libcares" @@ -475,8 +475,8 @@ AC_DEFUN([CURL_CHECK_OPTION_HTTPSRR], [ AC_MSG_CHECKING([whether to enable HTTPSRR support]) OPT_HTTPSRR="default" AC_ARG_ENABLE(httpsrr, -AS_HELP_STRING([--enable-httpsrr],[Enable HTTPSRR support]) -AS_HELP_STRING([--disable-httpsrr],[Disable HTTPSRR support]), +AS_HELP_STRING([--enable-httpsrr],[Enable HTTPSRR support (experimental)]) +AS_HELP_STRING([--disable-httpsrr],[Disable HTTPSRR support (experimental)]), OPT_HTTPSRR=$enableval) case "$OPT_HTTPSRR" in no) @@ -510,8 +510,8 @@ AC_DEFUN([CURL_CHECK_OPTION_ECH], [ AC_MSG_CHECKING([whether to enable ECH support]) OPT_ECH="default" AC_ARG_ENABLE(ech, -AS_HELP_STRING([--enable-ech],[Enable ECH support]) -AS_HELP_STRING([--disable-ech],[Disable ECH support]), +AS_HELP_STRING([--enable-ech],[Enable ECH support (experimental)]) +AS_HELP_STRING([--disable-ech],[Disable ECH support (experimental)]), OPT_ECH=$enableval) case "$OPT_ECH" in no) @@ -547,9 +547,9 @@ AC_DEFUN([CURL_CHECK_OPTION_SSLS_EXPORT], [ OPT_SSLS_EXPORT="default" AC_ARG_ENABLE(ssls-export, AS_HELP_STRING([--enable-ssls-export], - [Enable SSL session export support]) + [Enable SSL session export support (experimental)]) AS_HELP_STRING([--disable-ssls-export], - [Disable SSL session export support]), + [Disable SSL session export support (experimental)]), OPT_SSLS_EXPORT=$enableval) case "$OPT_SSLS_EXPORT" in no) diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index 9d80f2f5384a..3a273770168d 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -467,7 +467,7 @@ dnl Verify if alarm 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_alarm, then -dnl HAVE_ALARM will be defined. +dnl HAVE_ALARM is defined. AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ AC_REQUIRE([CURL_INCLUDES_UNISTD]) @@ -507,7 +507,7 @@ AC_DEFUN([CURL_CHECK_FUNC_ALARM], [ AC_LANG_PROGRAM([[ $curl_includes_unistd ]],[[ - if(alarm(0) != 0) + if(alarm(0)) return 1; ]]) ],[ @@ -552,7 +552,7 @@ dnl Verify if basename 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_basename, then -dnl HAVE_BASENAME will be defined. +dnl HAVE_BASENAME is defined. AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -598,7 +598,7 @@ AC_DEFUN([CURL_CHECK_FUNC_BASENAME], [ $curl_includes_libgen $curl_includes_unistd ]],[[ - if(basename(0) != 0) + if(basename(0)) return 1; ]]) ],[ @@ -643,7 +643,7 @@ dnl Verify if closesocket 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_closesocket, then -dnl HAVE_CLOSESOCKET will be defined. +dnl HAVE_CLOSESOCKET is defined. AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -658,7 +658,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(closesocket(0) != 0) + if(closesocket(0)) return 1; ]]) ],[ @@ -688,7 +688,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(closesocket(0) != 0) + if(closesocket(0)) return 1; ]]) ],[ @@ -733,7 +733,7 @@ dnl Verify if CloseSocket 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_closesocket_camel, -dnl then HAVE_CLOSESOCKET_CAMEL will be defined. +dnl then HAVE_CLOSESOCKET_CAMEL is defined. AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) @@ -749,7 +749,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(CloseSocket(0) != 0) + if(CloseSocket(0)) return 1; ]]) ],[ @@ -767,7 +767,7 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET_CAMEL], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(CloseSocket(0) != 0) + if(CloseSocket(0)) return 1; ]]) ],[ @@ -810,7 +810,7 @@ dnl Verify if fcntl 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_fcntl, then -dnl HAVE_FCNTL will be defined. +dnl HAVE_FCNTL is defined. AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ AC_REQUIRE([CURL_INCLUDES_FCNTL]) @@ -850,7 +850,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL], [ AC_LANG_PROGRAM([[ $curl_includes_fcntl ]],[[ - if(fcntl(0, 0, 0) != 0) + if(fcntl(0, 0, 0)) return 1; ]]) ],[ @@ -895,7 +895,7 @@ dnl ------------------------------------------------- dnl Verify if fcntl with status flag O_NONBLOCK is dnl available, can be compiled, and seems to work. If dnl all of these are true, then HAVE_FCNTL_O_NONBLOCK -dnl will be defined. +dnl is defined. AC_DEFUN([CURL_CHECK_FUNC_FCNTL_O_NONBLOCK], [ @@ -916,7 +916,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FCNTL_O_NONBLOCK], [ $curl_includes_fcntl ]],[[ int flags = 0; - if(fcntl(0, F_SETFL, flags | O_NONBLOCK) != 0) + if(fcntl(0, F_SETFL, flags | O_NONBLOCK)) return 1; ]]) ],[ @@ -959,7 +959,7 @@ dnl Verify if freeaddrinfo is available, prototyped, dnl and can be compiled. If all of these are true, dnl and usage has not been previously disallowed with dnl shell variable curl_disallow_freeaddrinfo, then -dnl HAVE_FREEADDRINFO will be defined. +dnl HAVE_FREEADDRINFO is defined. AC_DEFUN([CURL_CHECK_FUNC_FREEADDRINFO], [ AC_REQUIRE([CURL_INCLUDES_WS2TCPIP]) @@ -1055,7 +1055,7 @@ dnl Verify if fsetxattr 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_fsetxattr, then -dnl HAVE_FSETXATTR will be defined. +dnl HAVE_FSETXATTR is defined. AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_REQUIRE([CURL_INCLUDES_SYS_XATTR]) @@ -1097,7 +1097,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_LANG_PROGRAM([[ $curl_includes_sys_xattr ]],[[ - if(fsetxattr(0, "", 0, 0, 0) != 0) + if(fsetxattr(0, "", 0, 0, 0)) return 1; ]]) ],[ @@ -1115,7 +1115,7 @@ AC_DEFUN([CURL_CHECK_FUNC_FSETXATTR], [ AC_LANG_PROGRAM([[ $curl_includes_sys_xattr ]],[[ - if(fsetxattr(0, 0, 0, 0, 0, 0) != 0) + if(fsetxattr(0, 0, 0, 0, 0, 0)) return 1; ]]) ],[ @@ -1177,8 +1177,8 @@ dnl Verify if getaddrinfo 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_getaddrinfo, then -dnl HAVE_GETADDRINFO will be defined. Additionally when -dnl HAVE_GETADDRINFO gets defined this will also attempt +dnl HAVE_GETADDRINFO is defined. Additionally when +dnl HAVE_GETADDRINFO gets defined this also attempts dnl to find out if getaddrinfo happens to be thread-safe, dnl defining HAVE_GETADDRINFO_THREADSAFE when true. @@ -1205,7 +1205,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ $curl_includes_netdb ]],[[ struct addrinfo *ai = 0; - if(getaddrinfo(0, 0, 0, &ai) != 0) + if(getaddrinfo(0, 0, 0, &ai)) return 1; ]]) ],[ @@ -1240,7 +1240,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ $curl_includes_netdb ]],[[ struct addrinfo *ai = 0; - if(getaddrinfo(0, 0, 0, &ai) != 0) + if(getaddrinfo(0, 0, 0, &ai)) return 1; ]]) ],[ @@ -1408,7 +1408,7 @@ dnl Verify if gethostbyname_r is available, prototyped, dnl and can be compiled. If all of these are true, and dnl usage has not been previously disallowed with dnl shell variable curl_disallow_gethostbyname_r, then -dnl HAVE_GETHOSTBYNAME_R will be defined. +dnl HAVE_GETHOSTBYNAME_R is defined. AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ AC_REQUIRE([CURL_INCLUDES_NETDB]) @@ -1451,7 +1451,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(gethostbyname_r(0, 0, 0) != 0) + if(gethostbyname_r(0, 0, 0)) return 1; ]]) ],[ @@ -1470,7 +1470,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(gethostbyname_r(0, 0, 0, 0, 0) != 0) + if(gethostbyname_r(0, 0, 0, 0, 0)) return 1; ]]) ],[ @@ -1489,7 +1489,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTBYNAME_R], [ $curl_includes_netdb $curl_includes_bsdsocket ]],[[ - if(gethostbyname_r(0, 0, 0, 0, 0, 0) != 0) + if(gethostbyname_r(0, 0, 0, 0, 0, 0)) return 1; ]]) ],[ @@ -1553,7 +1553,7 @@ dnl Verify if gethostname 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_gethostname, then -dnl HAVE_GETHOSTNAME will be defined. +dnl HAVE_GETHOSTNAME is defined. AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -1574,7 +1574,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ $curl_includes_bsdsocket ]],[[ char s[1]; - if(gethostname((void *)s, 0) != 0) + if(gethostname((void *)s, 0)) return 1; ]]) ],[ @@ -1609,7 +1609,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ $curl_includes_bsdsocket ]],[[ char s[1]; - if(gethostname((void *)s, 0) != 0) + if(gethostname((void *)s, 0)) return 1; ]]) ],[ @@ -1641,7 +1641,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETHOSTNAME], [ int FUNCALLCONV gethostname($tst_arg1, $tst_arg2); ]],[[ char s[1]; - if(gethostname(($tst_arg1)s, 0) != 0) + if(gethostname(($tst_arg1)s, 0)) return 1; ]]) ],[ @@ -1689,7 +1689,7 @@ dnl Verify if getpeername 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_getpeername, then -dnl HAVE_GETPEERNAME will be defined. +dnl HAVE_GETPEERNAME is defined. AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -1709,7 +1709,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(getpeername(0, (void *)0, (void *)0) != 0) + if(getpeername(0, (void *)0, (void *)0)) return 1; ]]) ],[ @@ -1743,7 +1743,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETPEERNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(getpeername(0, (void *)0, (void *)0) != 0) + if(getpeername(0, (void *)0, (void *)0)) return 1; ]]) ],[ @@ -1787,7 +1787,7 @@ dnl Verify if getsockname 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_getsockname, then -dnl HAVE_GETSOCKNAME will be defined. +dnl HAVE_GETSOCKNAME is defined. AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -1807,7 +1807,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(getsockname(0, (void *)0, (void *)0) != 0) + if(getsockname(0, (void *)0, (void *)0)) return 1; ]]) ],[ @@ -1841,7 +1841,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(getsockname(0, (void *)0, (void *)0) != 0) + if(getsockname(0, (void *)0, (void *)0)) return 1; ]]) ],[ @@ -1886,7 +1886,7 @@ dnl Verify if getifaddrs 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_getifaddrs, then -dnl HAVE_GETIFADDRS will be defined. +dnl HAVE_GETIFADDRS is defined. AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -1928,7 +1928,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETIFADDRS], [ AC_LANG_PROGRAM([[ $curl_includes_ifaddrs ]],[[ - if(getifaddrs(0) != 0) + if(getifaddrs(0)) return 1; ]]) ],[ @@ -2004,7 +2004,7 @@ dnl Verify if gmtime_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_gmtime_r, then -dnl HAVE_GMTIME_R will be defined. +dnl HAVE_GMTIME_R is defined. AC_DEFUN([CURL_CHECK_FUNC_GMTIME_R], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2124,7 +2124,7 @@ 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. +dnl HAVE_LOCALTIME_R is defined. AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2168,7 +2168,7 @@ AC_DEFUN([CURL_CHECK_FUNC_LOCALTIME_R], [ ]],[[ time_t clock = 1170352587; struct tm result; - if(localtime_r(&clock, &result) != 0) + if(localtime_r(&clock, &result)) return 1; (void)result; ]]) @@ -2244,7 +2244,7 @@ dnl Verify if inet_ntop 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_inet_ntop, then -dnl HAVE_INET_NTOP will be defined. +dnl HAVE_INET_NTOP is defined. AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2289,7 +2289,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ ]],[[ char ipv4res[sizeof("255.255.255.255")]; unsigned char ipv4a[5] = ""; - if(inet_ntop(0, ipv4a, ipv4res, 0) != 0) + if(inet_ntop(0, ipv4a, ipv4res, 0)) return 1; ]]) ],[ @@ -2332,7 +2332,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ return 1; /* fail */ if(!ipv4ptr[0]) return 1; /* fail */ - if(memcmp(ipv4res, "192.168.100.1", 13) != 0) + if(memcmp(ipv4res, "192.168.100.1", 13)) return 1; /* fail */ /* - */ ipv6res[0] = '\0'; @@ -2356,7 +2356,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_NTOP], [ return 1; /* fail */ if(!ipv6ptr[0]) return 1; /* fail */ - if(memcmp(ipv6res, "fe80::214:4fff:fe0b:76c8", 24) != 0) + if(memcmp(ipv6res, "fe80::214:4fff:fe0b:76c8", 24)) return 1; /* fail */ /* - */ return 0; @@ -2405,7 +2405,7 @@ dnl Verify if inet_pton 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_inet_pton, then -dnl HAVE_INET_PTON will be defined. +dnl HAVE_INET_PTON is defined. AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ AC_REQUIRE([CURL_INCLUDES_STDLIB]) @@ -2450,7 +2450,7 @@ AC_DEFUN([CURL_CHECK_FUNC_INET_PTON], [ ]],[[ unsigned char ipv4a[4 + 1] = ""; const char *ipv4src = "192.168.100.1"; - if(inet_pton(0, ipv4src, ipv4a) != 0) + if(inet_pton(0, ipv4src, ipv4a)) return 1; ]]) ],[ @@ -2599,7 +2599,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL], [ AC_LANG_PROGRAM([[ $curl_includes_stropts ]],[[ - if(ioctl(0, 0, 0) != 0) + if(ioctl(0, 0, 0)) return 1; ]]) ],[ @@ -2643,7 +2643,7 @@ dnl ------------------------------------------------- dnl Verify if ioctl with the FIONBIO command is dnl available, can be compiled, and seems to work. If dnl all of these are true, then HAVE_IOCTL_FIONBIO -dnl will be defined. +dnl is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTL_FIONBIO], [ @@ -2657,7 +2657,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_FIONBIO], [ $curl_includes_stropts ]],[[ int flags = 0; - if(ioctl(0, FIONBIO, &flags) != 0) + if(ioctl(0, FIONBIO, &flags)) return 1; ]]) ],[ @@ -2699,7 +2699,7 @@ dnl ------------------------------------------------- dnl Verify if ioctl with the SIOCGIFADDR command is available, dnl struct ifreq is defined, they can be compiled, and seem to dnl work. If all of these are true, then HAVE_IOCTL_SIOCGIFADDR -dnl will be defined. +dnl is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTL_SIOCGIFADDR], [ @@ -2714,7 +2714,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTL_SIOCGIFADDR], [ #include ]],[[ struct ifreq ifr; - if(ioctl(0, SIOCGIFADDR, &ifr) != 0) + if(ioctl(0, SIOCGIFADDR, &ifr)) return 1; ]]) ],[ @@ -2757,7 +2757,7 @@ dnl Verify if ioctlsocket 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_ioctlsocket, then -dnl HAVE_IOCTLSOCKET will be defined. +dnl HAVE_IOCTLSOCKET is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -2772,7 +2772,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(ioctlsocket(0, 0, 0) != 0) + if(ioctlsocket(0, 0, 0)) return 1; ]]) ],[ @@ -2802,7 +2802,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET], [ AC_LANG_PROGRAM([[ $curl_includes_winsock2 ]],[[ - if(ioctlsocket(0, 0, 0) != 0) + if(ioctlsocket(0, 0, 0)) return 1; ]]) ],[ @@ -2847,7 +2847,7 @@ dnl ------------------------------------------------- dnl Verify if ioctlsocket with the FIONBIO command is dnl available, can be compiled, and seems to work. If dnl all of these are true, then HAVE_IOCTLSOCKET_FIONBIO -dnl will be defined. +dnl is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_FIONBIO], [ @@ -2861,7 +2861,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_FIONBIO], [ $curl_includes_winsock2 ]],[[ unsigned long flags = 0; - if(ioctlsocket(0, FIONBIO, &flags) != 0) + if(ioctlsocket(0, FIONBIO, &flags)) return 1; ]]) ],[ @@ -2904,7 +2904,7 @@ dnl Verify if IoctlSocket 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_ioctlsocket_camel, -dnl then HAVE_IOCTLSOCKET_CAMEL will be defined. +dnl then HAVE_IOCTLSOCKET_CAMEL is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) @@ -2918,7 +2918,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ AC_LANG_PROGRAM([[ $curl_includes_bsdsocket ]],[[ - if(IoctlSocket(0, 0, 0) != 0) + if(IoctlSocket(0, 0, 0)) return 1; ]]) ],[ @@ -2935,7 +2935,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL], [ AC_LANG_PROGRAM([[ $curl_includes_bsdsocket ]],[[ - if(IoctlSocket(0, 0, 0) != 0) + if(IoctlSocket(0, 0, 0)) return 1; ]]) ],[ @@ -2978,7 +2978,7 @@ dnl CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO dnl ------------------------------------------------- dnl Verify if IoctlSocket with FIONBIO command is available, dnl can be compiled, and seems to work. If all of these are -dnl true, then HAVE_IOCTLSOCKET_CAMEL_FIONBIO will be defined. +dnl true, then HAVE_IOCTLSOCKET_CAMEL_FIONBIO is defined. AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO], [ AC_REQUIRE([CURL_INCLUDES_BSDSOCKET]) @@ -2993,7 +2993,7 @@ AC_DEFUN([CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL_FIONBIO], [ $curl_includes_bsdsocket ]],[[ long flags = 0; - if(IoctlSocket(0, FIONBIO, &flags) != 0) + if(IoctlSocket(0, FIONBIO, &flags)) return 1; ]]) ],[ @@ -3036,7 +3036,7 @@ dnl Verify if memrchr 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_memrchr, then -dnl HAVE_MEMRCHR will be defined. +dnl HAVE_MEMRCHR is defined. AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -3064,7 +3064,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(memrchr("", 0, 0) != 0) + if(memrchr("", 0, 0)) return 1; ]]) ],[ @@ -3096,7 +3096,7 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(memrchr("", 0, 0) != 0) + if(memrchr("", 0, 0)) return 1; ]]) ],[ @@ -3141,7 +3141,7 @@ dnl Verify if sigaction 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_sigaction, then -dnl HAVE_SIGACTION will be defined. +dnl HAVE_SIGACTION is defined. AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ AC_REQUIRE([CURL_INCLUDES_SIGNAL]) @@ -3181,7 +3181,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGACTION], [ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(sigaction(0, 0, 0) != 0) + if(sigaction(0, 0, 0)) return 1; ]]) ],[ @@ -3226,7 +3226,7 @@ dnl Verify if siginterrupt 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_siginterrupt, then -dnl HAVE_SIGINTERRUPT will be defined. +dnl HAVE_SIGINTERRUPT is defined. AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ AC_REQUIRE([CURL_INCLUDES_SIGNAL]) @@ -3266,7 +3266,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGINTERRUPT], [ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(siginterrupt(0, 0) != 0) + if(siginterrupt(0, 0)) return 1; ]]) ],[ @@ -3311,7 +3311,7 @@ dnl Verify if signal 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_signal, then -dnl HAVE_SIGNAL will be defined. +dnl HAVE_SIGNAL is defined. AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ AC_REQUIRE([CURL_INCLUDES_SIGNAL]) @@ -3351,7 +3351,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGNAL], [ AC_LANG_PROGRAM([[ $curl_includes_signal ]],[[ - if(signal(0, 0) != 0) + if(signal(0, 0)) return 1; ]]) ],[ @@ -3396,7 +3396,7 @@ dnl Verify if sigsetjmp 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_sigsetjmp, then -dnl HAVE_SIGSETJMP will be defined. +dnl HAVE_SIGSETJMP is defined. AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ AC_REQUIRE([CURL_INCLUDES_SETJMP]) @@ -3425,7 +3425,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ $curl_includes_setjmp ]],[[ sigjmp_buf env; - if(sigsetjmp(env, 0) != 0) + if(sigsetjmp(env, 0)) return 1; ]]) ],[ @@ -3458,7 +3458,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SIGSETJMP], [ $curl_includes_setjmp ]],[[ sigjmp_buf env; - if(sigsetjmp(env, 0) != 0) + if(sigsetjmp(env, 0)) return 1; ]]) ],[ @@ -3503,7 +3503,7 @@ dnl Verify if socket 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_socket, then -dnl HAVE_SOCKET will be defined. +dnl HAVE_SOCKET is defined. AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2]) @@ -3521,7 +3521,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(socket(0, 0, 0) != 0) + if(socket(0, 0, 0)) return 1; ]]) ],[ @@ -3555,7 +3555,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_bsdsocket $curl_includes_sys_socket ]],[[ - if(socket(0, 0, 0) != 0) + if(socket(0, 0, 0)) return 1; ]]) ],[ @@ -3600,7 +3600,7 @@ dnl Verify if socketpair 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_socketpair, then -dnl HAVE_SOCKETPAIR will be defined. +dnl HAVE_SOCKETPAIR is defined. AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET]) @@ -3641,7 +3641,7 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ $curl_includes_sys_socket ]],[[ int sv[2]; - if(socketpair(0, 0, 0, sv) != 0) + if(socketpair(0, 0, 0, sv)) return 1; ]]) ],[ @@ -3686,7 +3686,7 @@ dnl Verify if strcasecmp 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_strcasecmp, then -dnl HAVE_STRCASECMP will be defined. +dnl HAVE_STRCASECMP is defined. AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -3726,7 +3726,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCASECMP], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(strcasecmp("", "") != 0) + if(strcasecmp("", "")) return 1; ]]) ],[ @@ -3770,7 +3770,7 @@ dnl Verify if strcmpi 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_strcmpi, then -dnl HAVE_STRCMPI will be defined. +dnl HAVE_STRCMPI is defined. AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -3810,7 +3810,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRCMPI], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(strcmpi(0, 0) != 0) + if(strcmpi(0, 0)) return 1; ]]) ],[ @@ -3854,7 +3854,7 @@ dnl ------------------------------------------------- dnl Verify if strerror_r is available, prototyped, can be compiled and dnl seems to work. If all of these are true, and usage has not been dnl previously disallowed with shell variable curl_disallow_strerror_r, -dnl then HAVE_STRERROR_R will be defined, as well as one of +dnl then HAVE_STRERROR_R is defined, as well as one of dnl HAVE_GLIBC_STRERROR_R or HAVE_POSIX_STRERROR_R. dnl dnl glibc-style strerror_r: @@ -3919,7 +3919,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ $curl_includes_string ]],[[ char s[1]; - if(strerror_r(0, s, 0) != 0) + if(strerror_r(0, s, 0)) return 1; ]]) ],[ @@ -3942,7 +3942,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ char *strerror_r(int errnum, char *workbuf, $arg3 bufsize); ]],[[ char s[1]; - if(strerror_r(0, s, 0) != 0) + if(strerror_r(0, s, 0)) return 1; (void)s; ]]) @@ -4005,7 +4005,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ int strerror_r(int errnum, char *resultbuf, $arg3 bufsize); ]],[[ char s[1]; - if(strerror_r(0, s, 0) != 0) + if(strerror_r(0, s, 0)) return 1; (void)s; ]]) @@ -4111,7 +4111,6 @@ AC_DEFUN([CURL_CHECK_FUNC_STRERROR_R], [ test "$tst_allow_strerror_r" = "unknown"; then AC_MSG_WARN([cannot determine strerror_r() style: edit lib/curl_config.h manually.]) fi - ]) @@ -4121,7 +4120,7 @@ dnl Verify if stricmp 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_stricmp, then -dnl HAVE_STRICMP will be defined. +dnl HAVE_STRICMP is defined. AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ AC_REQUIRE([CURL_INCLUDES_STRING]) @@ -4161,7 +4160,7 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ AC_LANG_PROGRAM([[ $curl_includes_string ]],[[ - if(stricmp(0, 0) != 0) + if(stricmp(0, 0)) return 1; ]]) ],[ @@ -4199,6 +4198,92 @@ AC_DEFUN([CURL_CHECK_FUNC_STRICMP], [ fi ]) + +dnl CURL_CHECK_FUNC_MEMSET_S +dnl ------------------------------------------------- +dnl Verify if memset_s 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_memset_s, then +dnl HAVE_MEMSET_S is defined. + +AC_DEFUN([CURL_CHECK_FUNC_MEMSET_S], [ + AC_REQUIRE([CURL_INCLUDES_STRING]) + + tst_links_memset_s="unknown" + tst_proto_memset_s="unknown" + tst_compi_memset_s="unknown" + tst_allow_memset_s="unknown" + + AC_MSG_CHECKING([if memset_s can be linked]) + AC_LINK_IFELSE([ + AC_LANG_FUNC_LINK_TRY([memset_s]) + ],[ + AC_MSG_RESULT([yes]) + tst_links_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_links_memset_s="no" + ]) + + if test "$tst_links_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s is prototyped]) + AC_EGREP_CPP([memset_s],[ + $curl_includes_string + ],[ + AC_MSG_RESULT([yes]) + tst_proto_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_proto_memset_s="no" + ]) + fi + + if test "$tst_proto_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s is compilable]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + $curl_includes_string + ]],[[ + char buf[2]; + if(memset_s(buf, sizeof(buf), 0, sizeof(buf))) + return 1; + ]]) + ],[ + AC_MSG_RESULT([yes]) + tst_compi_memset_s="yes" + ],[ + AC_MSG_RESULT([no]) + tst_compi_memset_s="no" + ]) + fi + + if test "$tst_compi_memset_s" = "yes"; then + AC_MSG_CHECKING([if memset_s usage allowed]) + if test "x$curl_disallow_memset_s" != "xyes"; then + AC_MSG_RESULT([yes]) + tst_allow_memset_s="yes" + else + AC_MSG_RESULT([no]) + tst_allow_memset_s="no" + fi + fi + + AC_MSG_CHECKING([if memset_s might be used]) + if test "$tst_links_memset_s" = "yes" && + test "$tst_proto_memset_s" = "yes" && + test "$tst_compi_memset_s" = "yes" && + test "$tst_allow_memset_s" = "yes"; then + AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(HAVE_MEMSET_S, 1, + [Define to 1 if you have the memset_s function.]) + curl_cv_func_memset_s="yes" + else + AC_MSG_RESULT([no]) + curl_cv_func_memset_s="no" + fi +]) + dnl CURL_RUN_IFELSE dnl ------------------------------------------------- dnl Wrapper macro to use instead of AC_RUN_IFELSE. It @@ -4271,6 +4356,7 @@ dnl CURL_ATOMIC dnl ------------------------------------------------------------- dnl Check if _Atomic works. But only check if stdatomic.h exists. dnl + AC_DEFUN([CURL_ATOMIC],[ AC_CHECK_HEADERS(stdatomic.h, [ AC_MSG_CHECKING([if _Atomic is available]) @@ -4352,5 +4438,4 @@ AC_DEFUN([CURL_SIZEOF], [ eval "$tname=$r" AC_DEFINE_UNQUOTED(TYPE, [$r], [Size of $1 in number of bytes]) - ]) diff --git a/m4/curl-gnutls.m4 b/m4/curl-gnutls.m4 index b8ee3f5780c9..3eab1c5d3192 100644 --- a/m4/curl-gnutls.m4 +++ b/m4/curl-gnutls.m4 @@ -30,114 +30,110 @@ AC_DEFUN([CURL_WITH_GNUTLS], [ if test "x$OPT_GNUTLS" != "xno"; then ssl_msg= - if test "x$OPT_GNUTLS" != "xno"; then - - addld="" - addlib="" - gtlslib="" - version="" - addcflags="" - - if test "x$OPT_GNUTLS" = "xyes"; then - dnl this is with no particular path given - CURL_CHECK_PKGCONFIG(gnutls) - - if test "$PKGCONFIG" != "no"; then - addlib=`$PKGCONFIG --libs-only-l gnutls` - addld=`$PKGCONFIG --libs-only-L gnutls` - addcflags=`$PKGCONFIG --cflags-only-I gnutls` - version=`$PKGCONFIG --modversion gnutls` - gtlslib=`echo $addld | $SED -e 's/^-L//'` - else - dnl without pkg-config, we try libgnutls-config as that was how it - dnl used to be done - check=`libgnutls-config --version 2>/dev/null` - if test -n "$check"; then - addlib=`libgnutls-config --libs` - addcflags=`libgnutls-config --cflags` - version=`libgnutls-config --version` - gtlslib=`libgnutls-config --prefix`/lib$libsuff - fi - fi + addld="" + addlib="" + gtlslib="" + version="" + addcflags="" + + if test "x$OPT_GNUTLS" = "xyes"; then + dnl this is with no particular path given + CURL_CHECK_PKGCONFIG(gnutls) + + if test "$PKGCONFIG" != "no"; then + addlib=`$PKGCONFIG --libs-only-l gnutls` + addld=`$PKGCONFIG --libs-only-L gnutls` + addcflags=`$PKGCONFIG --cflags-only-I gnutls` + version=`$PKGCONFIG --modversion gnutls` + gtlslib=`echo $addld | $SED -e 's/^-L//'` else - 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` + dnl without pkg-config, we try libgnutls-config as that was how it + dnl used to be done + check=`libgnutls-config --version 2>/dev/null` if test -n "$check"; then - addlib=`$cfg --libs` - addcflags=`$cfg --cflags` - version=`$cfg --version` - gtlslib=`$cfg --prefix`/lib$libsuff - else - dnl without pkg-config and libgnutls-config, we guess a lot! - addlib=-lgnutls - addld=-L$OPT_GNUTLS/lib$libsuff - addcflags=-I$OPT_GNUTLS/include - dnl we just do not know - version="" - gtlslib=$OPT_GNUTLS/lib$libsuff + addlib=`libgnutls-config --libs` + addcflags=`libgnutls-config --cflags` + version=`libgnutls-config --version` + gtlslib=`libgnutls-config --prefix`/lib$libsuff fi fi - - if test -z "$version"; then - dnl lots of efforts, still no go - version="unknown" + else + 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` + if test -n "$check"; then + addlib=`$cfg --libs` + addcflags=`$cfg --cflags` + version=`$cfg --version` + gtlslib=`$cfg --prefix`/lib$libsuff + else + dnl without pkg-config and libgnutls-config, we guess a lot! + addlib=-lgnutls + addld=-L$OPT_GNUTLS/lib$libsuff + addcflags=-I$OPT_GNUTLS/include + dnl we do not know + version="" + gtlslib=$OPT_GNUTLS/lib$libsuff fi + fi - if test -n "$addlib"; then + if test -z "$version"; then + dnl lots of efforts, still no go + version="unknown" + fi - CLEANLIBS="$LIBS" - CLEANCPPFLAGS="$CPPFLAGS" - CLEANLDFLAGS="$LDFLAGS" - CLEANLDFLAGSPC="$LDFLAGSPC" + if test -n "$addlib"; then - LIBS="$addlib $LIBS" - LDFLAGS="$LDFLAGS $addld" - LDFLAGSPC="$LDFLAGSPC $addld" - if test "$addcflags" != "-I/usr/include"; then - CPPFLAGS="$CPPFLAGS $addcflags" - fi + CLEANLIBS="$LIBS" + CLEANCPPFLAGS="$CPPFLAGS" + CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" - 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" - LDFLAGS="$CLEANLDFLAGS" - LDFLAGSPC="$CLEANLDFLAGSPC" - ]) - - 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 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 "$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 + LIBS="$addlib $LIBS" + LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" + fi + + 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" + LDFLAGS="$CLEANLDFLAGS" + LDFLAGSPC="$CLEANLDFLAGSPC" + ]) + + 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 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 "$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 - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gnutls" fi + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gnutls" fi - - fi dnl GNUTLS not disabled + fi test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" -fi +fi dnl GnuTLS not disabled dnl dnl Check which crypto backend GnuTLS uses @@ -145,7 +141,7 @@ dnl if test "$GNUTLS_ENABLED" = "1"; then USE_GNUTLS_NETTLE= dnl First check if we can detect either crypto library via transitive linking - AC_CHECK_LIB(gnutls, nettle_MD5Init, [ USE_GNUTLS_NETTLE=1 ]) + AC_CHECK_LIB(gnutls, nettle_md5_init, [ USE_GNUTLS_NETTLE=1 ]) dnl If not, try linking directly to both of them to see if they are available if test -z "$USE_GNUTLS_NETTLE"; then @@ -174,7 +170,7 @@ if test "$GNUTLS_ENABLED" = "1"; then CPPFLAGS="$CPPFLAGS $addcflags" fi - AC_CHECK_LIB(nettle, nettle_MD5Init, + AC_CHECK_LIB(nettle, nettle_md5_init, [ USE_GNUTLS_NETTLE=1 ], diff --git a/m4/curl-mbedtls.m4 b/m4/curl-mbedtls.m4 index 7c5bccd22983..0c19f0723717 100644 --- a/m4/curl-mbedtls.m4 +++ b/m4/curl-mbedtls.m4 @@ -33,90 +33,85 @@ if test "x$OPT_MBEDTLS" != "xno"; then _ldflagspc=$LDFLAGSPC ssl_msg= - if test "x$OPT_MBEDTLS" != "xno"; then - - if test "x$OPT_MBEDTLS" = "xyes"; then - OPT_MBEDTLS="" + if test "x$OPT_MBEDTLS" = "xyes"; then + OPT_MBEDTLS="" + fi + + if test -z "$OPT_MBEDTLS"; then + dnl check for lib first without setting any new path + + AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, + dnl libmbedtls found, set the variable + [ + AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) + MBEDTLS_ENABLED=1 + USE_MBEDTLS="yes" + ssl_msg="mbedTLS" + test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], [], -lmbedx509 -lmbedcrypto) + fi + + addld="" + addlib="" + addcflags="" + mbedtlslib="" + + if test "$USE_MBEDTLS" != "yes" && test -n "$OPT_MBEDTLS"; then + dnl add the path and test again + addld=-L$OPT_MBEDTLS/lib$libsuff + addcflags=-I$OPT_MBEDTLS/include + mbedtlslib=$OPT_MBEDTLS/lib$libsuff + + LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" fi - if test -z "$OPT_MBEDTLS"; then - dnl check for lib first without setting any new path - - AC_CHECK_LIB(mbedtls, mbedtls_havege_init, - dnl libmbedtls found, set the variable + AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, [ - AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) - MBEDTLS_ENABLED=1 - USE_MBEDTLS="yes" - ssl_msg="mbedTLS" - test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes - ], [], -lmbedx509 -lmbedcrypto) - fi - - addld="" - addlib="" - addcflags="" - mbedtlslib="" - - if test "$USE_MBEDTLS" != "yes"; then - dnl add the path and test again - addld=-L$OPT_MBEDTLS/lib$libsuff - addcflags=-I$OPT_MBEDTLS/include - mbedtlslib=$OPT_MBEDTLS/lib$libsuff - - LDFLAGS="$LDFLAGS $addld" - LDFLAGSPC="$LDFLAGSPC $addld" - if test "$addcflags" != "-I/usr/include"; then - CPPFLAGS="$CPPFLAGS $addcflags" + AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) + MBEDTLS_ENABLED=1 + USE_MBEDTLS="yes" + ssl_msg="mbedTLS" + test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], + [ + CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags + LDFLAGSPC=$_ldflagspc + ], -lmbedx509 -lmbedcrypto) + fi + + 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 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 "$cross_compiling" != "yes"; then + CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$mbedtlslib" + export CURL_LIBRARY_PATH + AC_MSG_NOTICE([Added $mbedtlslib to CURL_LIBRARY_PATH]) fi - - AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, - [ - AC_DEFINE(USE_MBEDTLS, 1, [if mbedTLS is enabled]) - MBEDTLS_ENABLED=1 - USE_MBEDTLS="yes" - ssl_msg="mbedTLS" - test "mbedtls" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes - ], - [ - CPPFLAGS=$_cppflags - LDFLAGS=$_ldflags - LDFLAGSPC=$_ldflagspc - ], -lmbedx509 -lmbedcrypto) fi - - 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 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 "$cross_compiling" != "yes"; then - CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$mbedtlslib" - export CURL_LIBRARY_PATH - AC_MSG_NOTICE([Added $mbedtlslib to CURL_LIBRARY_PATH]) - fi - fi - dnl FIXME: Enable when mbedTLS was detected via pkg-config - 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 + dnl FIXME: Enable when mbedTLS was detected via pkg-config + if false; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mbedtls mbedx509 mbedcrypto" fi - fi dnl mbedTLS not disabled + 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 test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" -fi - +fi dnl mbedTLS not disabled ]) diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index 256037b19a76..816e7631fae5 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -33,7 +33,7 @@ AC_DEFUN([CURL_WITH_OPENSSL], [ if test "x$OPT_OPENSSL" != "xno"; then ssl_msg= - dnl backup the pre-ssl variables + dnl backup the pre-detection variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" @@ -80,7 +80,7 @@ if test "x$OPT_OPENSSL" != "xno"; then dnl the user told us to look OPENSSL_PCDIR="$OPT_OPENSSL/lib/pkgconfig" if test -f "$OPENSSL_PCDIR/openssl.pc"; then - AC_MSG_NOTICE([PKG_CONFIG_LIBDIR will be set to "$OPENSSL_PCDIR"]) + AC_MSG_NOTICE([PKG_CONFIG_LIBDIR is set to "$OPENSSL_PCDIR"]) PKGTEST="yes" fi @@ -88,7 +88,7 @@ if test "x$OPT_OPENSSL" != "xno"; then 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"]) + AC_MSG_NOTICE([PKG_CONFIG_LIBDIR is set to "$OPENSSL_PCDIR"]) PKGTEST="yes" fi fi @@ -220,10 +220,6 @@ if test "x$OPT_OPENSSL" != "xno"; then if test "$OPENSSL_ENABLED" != "1"; then LIBS="$CLEANLIBS" - fi - - 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 @@ -231,36 +227,36 @@ if test "x$OPT_OPENSSL" != "xno"; then if test "$OPENSSL_ENABLED" = "1"; then dnl These can only exist if OpenSSL exists - AC_MSG_CHECKING([for BoringSSL]) + AC_MSG_CHECKING([for AWS-LC]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include ]],[[ - #ifndef OPENSSL_IS_BORINGSSL - #error not boringssl + #ifndef OPENSSL_IS_AWSLC + #error not AWS-LC #endif ]]) ],[ AC_MSG_RESULT([yes]) - ssl_msg="BoringSSL" - OPENSSL_IS_BORINGSSL=1 + ssl_msg="AWS-LC" + OPENSSL_IS_AWSLC=1 ],[ AC_MSG_RESULT([no]) ]) - AC_MSG_CHECKING([for AWS-LC]) + AC_MSG_CHECKING([for BoringSSL]) AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include ]],[[ - #ifndef OPENSSL_IS_AWSLC - #error not AWS-LC + #ifndef OPENSSL_IS_BORINGSSL + #error not BoringSSL #endif ]]) ],[ AC_MSG_RESULT([yes]) - ssl_msg="AWS-LC" - OPENSSL_IS_AWSLC=1 + ssl_msg="BoringSSL" + OPENSSL_IS_BORINGSSL=1 ],[ AC_MSG_RESULT([no]) ]) @@ -315,7 +311,7 @@ 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 runtime + 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 "$cross_compiling" != "yes"; then @@ -330,15 +326,14 @@ if test "x$OPT_OPENSSL" != "xno"; then fi fi - test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" -fi + if 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 -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 + test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" +fi dnl OpenSSL not disabled if test "$OPENSSL_ENABLED" = "1"; then dnl --- @@ -385,16 +380,15 @@ if test "$OPENSSL_ENABLED" = "1"; then ]) dnl --- - dnl Whether the OpenSSL configuration will be loaded automatically + dnl Whether the OpenSSL configuration is 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" = "xno"; then AC_MSG_NOTICE([automatic loading of OpenSSL configuration disabled]) - AC_DEFINE(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG, 1, [if the OpenSSL configuration will not be loaded automatically]) + AC_DEFINE(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG, 1, [if the OpenSSL configuration is not loaded automatically]) fi ]) - fi ]) diff --git a/m4/curl-override.m4 b/m4/curl-override.m4 index 79b24567ac98..59e6548dbd35 100644 --- a/m4/curl-override.m4 +++ b/m4/curl-override.m4 @@ -28,7 +28,7 @@ dnl serial 7 dnl CURL_OVERRIDE_AUTOCONF dnl ------------------------------------------------- dnl Placing a call to this macro in configure.ac after -dnl the one to AC_INIT will make macros in this file +dnl the one to AC_INIT makes macros in this file dnl visible to the rest of the compilation overriding dnl those from Autoconf. diff --git a/m4/curl-reentrant.m4 b/m4/curl-reentrant.m4 index 6ac731bed9f1..eb585ad64d27 100644 --- a/m4/curl-reentrant.m4 +++ b/m4/curl-reentrant.m4 @@ -320,10 +320,10 @@ AC_DEFUN([CURL_CHECK_NEED_THREAD_SAFE_SYSTEM], [ dnl CURL_CONFIGURE_FROM_NOW_ON_WITH_REENTRANT dnl ------------------------------------------------- dnl This macro ensures that configuration tests done -dnl after this will execute with preprocessor symbol -dnl _REENTRANT defined. This macro also ensures that -dnl the generated config file defines NEED_REENTRANT -dnl and that in turn curl_setup.h will define _REENTRANT. +dnl after this execute with preprocessor symbol _REENTRANT +dnl defined. This macro also ensures that the generated +dnl config file defines NEED_REENTRANT and that in turn +dnl curl_setup.h defines _REENTRANT. dnl Internal macro for CURL_CONFIGURE_REENTRANT. AC_DEFUN([CURL_CONFIGURE_FROM_NOW_ON_WITH_REENTRANT], [ @@ -340,10 +340,10 @@ _EOF dnl CURL_CONFIGURE_FROM_NOW_ON_WITH_THREAD_SAFE dnl ------------------------------------------------- dnl This macro ensures that configuration tests done -dnl after this will execute with preprocessor symbol -dnl _THREAD_SAFE defined. This macro also ensures that -dnl the generated config file defines NEED_THREAD_SAFE -dnl and that in turn curl_setup.h will define _THREAD_SAFE. +dnl after this execute with preprocessor symbol_THREAD_SAFE +dnl defined. This macro also ensures that the generated +dnl config file defines NEED_THREAD_SAFE and that in turn +dnl curl_setup.h defines _THREAD_SAFE. dnl Internal macro for CURL_CONFIGURE_THREAD_SAFE. AC_DEFUN([CURL_CONFIGURE_FROM_NOW_ON_WITH_THREAD_SAFE], [ diff --git a/m4/curl-rustls.m4 b/m4/curl-rustls.m4 index 2a035680ffcf..86bed3c32315 100644 --- a/m4/curl-rustls.m4 +++ b/m4/curl-rustls.m4 @@ -30,7 +30,7 @@ dnl ---------------------------------------------------- if test "x$OPT_RUSTLS" != "xno"; then ssl_msg= - dnl backup the pre-ssl variables + dnl backup the pre-detection variables CLEANLDFLAGS="$LDFLAGS" CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" @@ -57,7 +57,7 @@ if test "x$OPT_RUSTLS" != "xno"; then RUSTLS_PCDIR="$PREFIX_RUSTLS/lib/pkgconfig" if test -f "$RUSTLS_PCDIR/rustls.pc"; then - AC_MSG_NOTICE([PKG_CONFIG_LIBDIR will be set to "$RUSTLS_PCDIR"]) + AC_MSG_NOTICE([PKG_CONFIG_LIBDIR is set to "$RUSTLS_PCDIR"]) PKGTEST="yes" fi @@ -65,7 +65,7 @@ if test "x$OPT_RUSTLS" != "xno"; then 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"]) + AC_MSG_NOTICE([PKG_CONFIG_LIBDIR is set to "$RUSTLS_PCDIR"]) PKGTEST="yes" fi fi @@ -95,7 +95,7 @@ if test "x$OPT_RUSTLS" != "xno"; then SSL_CPPFLAGS="-I$PREFIX_RUSTLS/include" fi - dnl we will verify AC_CHECK_LIB later on + dnl we verify AC_CHECK_LIB later on AC_DEFINE(USE_RUSTLS, 1, [if Rustls is enabled]) USE_RUSTLS="yes" fi @@ -185,13 +185,10 @@ 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" && - test "$RUSTLS_ENABLED" != "1"; then + if test "$RUSTLS_ENABLED" != "1"; then AC_MSG_NOTICE([OPT_RUSTLS: $OPT_RUSTLS]) AC_MSG_NOTICE([RUSTLS_ENABLED: $RUSTLS_ENABLED]) AC_MSG_ERROR([--with-rustls was given but Rustls could not be detected]) fi fi ]) - -RUSTLS_ENABLED diff --git a/m4/curl-schannel.m4 b/m4/curl-schannel.m4 index 3d0385347c10..e7358fbf62f2 100644 --- a/m4/curl-schannel.m4 +++ b/m4/curl-schannel.m4 @@ -26,8 +26,7 @@ AC_DEFUN([CURL_WITH_SCHANNEL], [ AC_MSG_CHECKING([whether to enable Windows native SSL/TLS]) if test "x$OPT_SCHANNEL" != "xno"; then ssl_msg= - if test "x$OPT_SCHANNEL" != "xno" && - test "$curl_cv_native_windows" = "yes"; then + if test "$curl_cv_native_windows" = "yes"; then if test "$curl_cv_winuwp" = "yes"; then AC_MSG_ERROR([UWP does not support Schannel.]) fi diff --git a/m4/curl-wolfssl.m4 b/m4/curl-wolfssl.m4 index 1d7b46721b32..a18659605e77 100644 --- a/m4/curl-wolfssl.m4 +++ b/m4/curl-wolfssl.m4 @@ -43,134 +43,129 @@ if test "$OPT_WOLFSSL" != "no"; then ssl_msg= - if test "$OPT_WOLFSSL" != "no"; then - - if test "$OPT_WOLFSSL" = "yes"; then - OPT_WOLFSSL="" + if test "$OPT_WOLFSSL" = "yes"; then + OPT_WOLFSSL="" + fi + + dnl try pkg-config magic + CURL_CHECK_PKGCONFIG(wolfssl, [$wolfpkg]) + AC_MSG_NOTICE([Check directory $wolfpkg]) + + addld="" + addlib="" + addcflags="" + if test "$PKGCONFIG" != "no"; then + addlib=`CURL_EXPORT_PCDIR([$wolfpkg]) + $PKGCONFIG --libs-only-l wolfssl` + addld=`CURL_EXPORT_PCDIR([$wolfpkg]) + $PKGCONFIG --libs-only-L wolfssl` + addcflags=`CURL_EXPORT_PCDIR([$wolfpkg]) + $PKGCONFIG --cflags-only-I wolfssl` + version=`CURL_EXPORT_PCDIR([$wolfpkg]) + $PKGCONFIG --modversion wolfssl` + wolfssllibpath=`echo $addld | $SED -e 's/^-L//'` + else + addlib=-lwolfssl + dnl use system defaults if user does not supply a path + if test -n "$OPT_WOLFSSL"; then + addld=-L$OPT_WOLFSSL/lib$libsuff + addcflags=-I$OPT_WOLFSSL/include + wolfssllibpath=$OPT_WOLFSSL/lib$libsuff fi - - dnl try pkg-config magic - CURL_CHECK_PKGCONFIG(wolfssl, [$wolfpkg]) - AC_MSG_NOTICE([Check directory $wolfpkg]) - - addld="" - addlib="" - addcflags="" - if test "$PKGCONFIG" != "no"; then - addlib=`CURL_EXPORT_PCDIR([$wolfpkg]) - $PKGCONFIG --libs-only-l wolfssl` - addld=`CURL_EXPORT_PCDIR([$wolfpkg]) - $PKGCONFIG --libs-only-L wolfssl` - addcflags=`CURL_EXPORT_PCDIR([$wolfpkg]) - $PKGCONFIG --cflags-only-I wolfssl` - version=`CURL_EXPORT_PCDIR([$wolfpkg]) - $PKGCONFIG --modversion wolfssl` - wolfssllibpath=`echo $addld | $SED -e 's/^-L//'` - else - addlib=-lwolfssl - dnl use system defaults if user does not supply a path - if test -n "$OPT_WOLFSSL"; then - addld=-L$OPT_WOLFSSL/lib$libsuff - addcflags=-I$OPT_WOLFSSL/include - wolfssllibpath=$OPT_WOLFSSL/lib$libsuff - fi + fi + + if test "$curl_cv_apple" = "yes"; then + addlib="$addlib -framework Security -framework CoreFoundation" + else + addlib="$addlib -lm" + fi + + if test "$USE_WOLFSSL" != "yes"; then + CPPFLAGS="$CPPFLAGS -DWOLFSSL_OPTIONS_IGNORE_SYS" + + LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" + AC_MSG_NOTICE([Add $addld to LDFLAGS]) + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" + AC_MSG_NOTICE([Add $addcflags to CPPFLAGS]) fi - if test "$curl_cv_apple" = "yes"; then - addlib="$addlib -framework Security -framework CoreFoundation" - else - addlib="$addlib -lm" + my_ac_save_LIBS="$LIBS" + LIBS="$addlib $LIBS" + AC_MSG_NOTICE([Add $addlib to LIBS]) + + AC_MSG_CHECKING([for wolfSSL_Init in -lwolfssl]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ + #include + #include + ]],[[ + return wolfSSL_Init(); + ]]) + ],[ + AC_MSG_RESULT(yes) + AC_DEFINE(USE_WOLFSSL, 1, [if wolfSSL is enabled]) + WOLFSSL_ENABLED=1 + USE_WOLFSSL="yes" + ssl_msg="wolfSSL" + test "wolfssl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], + [ + AC_MSG_RESULT(no) + CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags + LDFLAGSPC=$_ldflagspc + wolfssllibpath="" + ]) + LIBS="$my_ac_save_LIBS" + fi + + if test "$USE_WOLFSSL" = "yes"; then + AC_MSG_NOTICE([detected wolfSSL]) + check_for_ca_bundle=1 + + 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 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_BIO_new \ + 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 "$ac_cv_func_wc_Des_EcbEncrypt" = "yes"; then + HAVE_WC_DES_ECBENCRYPT=1 fi - if test "$USE_WOLFSSL" != "yes"; then - CPPFLAGS="$CPPFLAGS -DWOLFSSL_OPTIONS_IGNORE_SYS" - - LDFLAGS="$LDFLAGS $addld" - LDFLAGSPC="$LDFLAGSPC $addld" - AC_MSG_NOTICE([Add $addld to LDFLAGS]) - if test "$addcflags" != "-I/usr/include"; then - CPPFLAGS="$CPPFLAGS $addcflags" - AC_MSG_NOTICE([Add $addcflags to CPPFLAGS]) - fi - - my_ac_save_LIBS="$LIBS" - LIBS="$addlib $LIBS" - AC_MSG_NOTICE([Add $addlib to LIBS]) - - AC_MSG_CHECKING([for wolfSSL_Init in -lwolfssl]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - #include - #include - ]],[[ - return wolfSSL_Init(); - ]]) - ],[ - AC_MSG_RESULT(yes) - AC_DEFINE(USE_WOLFSSL, 1, [if wolfSSL is enabled]) - WOLFSSL_ENABLED=1 - USE_WOLFSSL="yes" - ssl_msg="wolfSSL" - test "wolfssl" != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes - ], - [ - AC_MSG_RESULT(no) - CPPFLAGS=$_cppflags - LDFLAGS=$_ldflags - LDFLAGSPC=$_ldflagspc - wolfssllibpath="" - ]) - LIBS="$my_ac_save_LIBS" + dnl if this symbol is present, we can make use of BIO filter chains + if test "$ac_cv_func_wolfSSL_BIO_new" = "yes"; then + HAVE_WOLFSSL_BIO_NEW=1 fi - if test "$USE_WOLFSSL" = "yes"; then - AC_MSG_NOTICE([detected wolfSSL]) - check_for_ca_bundle=1 - - 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 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_BIO_new \ - 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 "$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 "$ac_cv_func_wolfSSL_BIO_new" = "yes"; then - HAVE_WOLFSSL_BIO_NEW=1 + if test -n "$wolfssllibpath"; then + 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 "$cross_compiling" != "yes"; then + CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$wolfssllibpath" + export CURL_LIBRARY_PATH + AC_MSG_NOTICE([Added $wolfssllibpath to CURL_LIBRARY_PATH]) fi - - if test -n "$wolfssllibpath"; then - 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 "$cross_compiling" != "yes"; then - CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$wolfssllibpath" - export CURL_LIBRARY_PATH - AC_MSG_NOTICE([Added $wolfssllibpath to CURL_LIBRARY_PATH]) - fi - fi - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE wolfssl" - else - AC_MSG_ERROR([--with-wolfssl but wolfSSL was not found or does not work]) fi - - fi dnl wolfSSL not disabled + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE wolfssl" + else + AC_MSG_ERROR([--with-wolfssl but wolfSSL was not found or does not work]) + fi test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" -fi - +fi dnl wolfSSL not disabled ]) diff --git a/m4/xc-lt-iface.m4 b/m4/xc-lt-iface.m4 index 0d8b0ef31fc4..9f79fdb95210 100644 --- a/m4/xc-lt-iface.m4 +++ b/m4/xc-lt-iface.m4 @@ -111,7 +111,7 @@ esac 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 of non-PIC compiled objects fails 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'. @@ -183,10 +183,10 @@ dnl xc_lt_build_static m4_define([_XC_CHECK_LT_BUILD_LIBRARIES], [ + # -# Verify if finally libtool shared libraries will be built +# Verify if finally libtool shared libraries are built # - case "x$enable_shared" in @%:@ (( xyes | xno) xc_lt_build_shared=$enable_shared @@ -197,9 +197,8 @@ case "x$enable_shared" in @%:@ (( esac # -# Verify if finally libtool static libraries will be built +# Verify if finally libtool static libraries are built # - case "x$enable_static" in @%:@ (( xyes | xno) xc_lt_build_static=$enable_static @@ -362,10 +361,10 @@ dnl xc_lt_build_static_only m4_define([_XC_CHECK_LT_BUILD_SINGLE_VERSION], [ + # -# Verify if libtool shared libraries will be built while static not built +# Verify if libtool shared libraries are 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 @@ -376,9 +375,8 @@ fi AC_MSG_RESULT([$xc_lt_build_shared_only]) # -# Verify if libtool static libraries will be built while shared not built +# Verify if libtool static libraries are built while shared not built # - AC_MSG_CHECKING([whether to build static libraries only]) if test "$xc_lt_build_static" = "yes" && test "$xc_lt_build_shared" = "no"; then diff --git a/m4/zz40-xc-ovr.m4 b/m4/zz40-xc-ovr.m4 index 5d2b2d0b727d..2713dcc8cb52 100644 --- a/m4/zz40-xc-ovr.m4 +++ b/m4/zz40-xc-ovr.m4 @@ -606,7 +606,7 @@ 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 -dnl generated configure script will be done where first +dnl generated configure script is done where first dnl direct or indirect usage of this macro happens. AC_DEFUN([XC_CHECK_PATH_SEPARATOR], diff --git a/m4/zz50-xc-ovr.m4 b/m4/zz50-xc-ovr.m4 index 155eb7f9b494..563c5ab25a19 100644 --- a/m4/zz50-xc-ovr.m4 +++ b/m4/zz50-xc-ovr.m4 @@ -52,8 +52,8 @@ m4_define([AC_LIBTOOL_LANG_GCJ_CONFIG],[:]) dnl XC_OVR_ZZ50 dnl ------------------------------------------------- -dnl Placing a call to this macro in configure.ac will -dnl make macros in this file visible to other macros +dnl Placing a call to this macro in configure.ac +dnl makes macros in this file visible to other macros dnl used for same configure script, overriding those dnl provided elsewhere. diff --git a/projects/OS400/.checksrc b/projects/OS400/.checksrc index 3bd88ff1e7db..e27d5729de0b 100644 --- a/projects/OS400/.checksrc +++ b/projects/OS400/.checksrc @@ -2,7 +2,7 @@ # # SPDX-License-Identifier: curl -# Possibly not what we want, but cannot test, just silence the warnings +# Possibly not what we want, but cannot test, thus silence the warnings allowfunc calloc allowfunc free allowfunc malloc diff --git a/projects/OS400/ccsidcurl.c b/projects/OS400/ccsidcurl.c index 0982eed639f7..cba89f8642ea 100644 --- a/projects/OS400/ccsidcurl.c +++ b/projects/OS400/ccsidcurl.c @@ -537,7 +537,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) { va_list arg; void *paramp; - CURLcode ret; + CURLcode result; struct Curl_easy *data; /* WARNING: unlike curl_easy_getinfo(), the strings returned by this @@ -546,9 +546,9 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) data = (struct Curl_easy *)curl; va_start(arg, info); paramp = va_arg(arg, void *); - ret = Curl_getinfo(data, info, paramp); + result = Curl_getinfo(data, info, paramp); - if(ret == CURLE_OK) { + if(result == CURLE_OK) { unsigned int ccsid; char **cpp; struct curl_slist **slp; @@ -565,7 +565,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) *cpp = dynconvert(ccsid, *cpp, -1, ASCII_CCSID, NULL); if(!*cpp) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } break; @@ -576,15 +576,14 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) case CURLINFO_CERTINFO: cipf = *(struct curl_certinfo **)paramp; if(cipf) { - cipt = (struct curl_certinfo *)malloc(sizeof(*cipt)); + cipt = malloc(sizeof(*cipt)); if(!cipt) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else { - cipt->certinfo = - (struct curl_slist **)calloc(cipf->num_of_certs + 1, - sizeof(struct curl_slist *)); + cipt->certinfo = calloc(cipf->num_of_certs + 1, + sizeof(struct curl_slist *)); if(!cipt->certinfo) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; else { int i; @@ -594,13 +593,13 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) if(!(cipt->certinfo[i] = slist_convert(ccsid, cipf->certinfo[i], ASCII_CCSID))) { - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; break; } } } - if(ret != CURLE_OK) { + if(result != CURLE_OK) { curl_certinfo_free_all(cipt); cipt = (struct curl_certinfo *)NULL; } @@ -620,7 +619,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) if(*slp) { *slp = slist_convert(ccsid, *slp, ASCII_CCSID); if(!*slp) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } break; } @@ -628,7 +627,7 @@ CURLcode curl_easy_getinfo_ccsid(CURL *curl, CURLINFO info, ...) } va_end(arg); - return ret; + return result; } static int Curl_is_formadd_string(CURLformoption option) diff --git a/projects/OS400/curlcl.c b/projects/OS400/curlcl.c index 7a7f3c6459d6..8085307f31ca 100644 --- a/projects/OS400/curlcl.c +++ b/projects/OS400/curlcl.c @@ -149,7 +149,7 @@ int main(int argsc, struct arguments *args) if(!exitcode) { /* Allocate space for parsed arguments. */ - argv = (char **)malloc((argc + 1) * sizeof(*argv) + argsize); + argv = malloc((argc + 1) * sizeof(*argv) + argsize); if(!argv) { fputs("Memory allocation error\n", stderr); exitcode = -2; diff --git a/projects/OS400/curlmain.c b/projects/OS400/curlmain.c index 54ca865264dd..690e8c622177 100644 --- a/projects/OS400/curlmain.c +++ b/projects/OS400/curlmain.c @@ -59,7 +59,7 @@ int main(int argc, char **argv) size_t inbytesleft; size_t outbytesleft; char dummybuf[128]; - /* To/From codes are 32 byte long strings with + /* To/From codes are 32-byte strings with reserved fields initialized to ZEROs */ const char tocode[32] = { "IBMCCSID01208" }; /* Use UTF-8. */ const char fromcode[32] = { "IBMCCSID000000000010" }; @@ -86,7 +86,7 @@ int main(int argc, char **argv) } /* Allocate memory for the ASCII arguments and vector. */ - argv = (char **)malloc((argc + 1) * sizeof(*argv) + bytecount); + argv = malloc((argc + 1) * sizeof(*argv) + bytecount); /* Build the vector and convert argument encoding. */ outbuf = (char *)(argv + argc + 1); diff --git a/projects/OS400/os400sys.c b/projects/OS400/os400sys.c index 36ed2480ead6..fc7ae3c6122f 100644 --- a/projects/OS400/os400sys.c +++ b/projects/OS400/os400sys.c @@ -370,6 +370,10 @@ static int Curl_gss_convert_in_place(OM_uint32 *minor_status, gss_buffer_t buf) return 0; } +/* Max string input length is a precaution against abuse and to detect junk + input easier and better. */ +#define CURL_MAX_INPUT_LENGTH 8000000 + 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) { @@ -381,7 +385,14 @@ OM_uint32 Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, return gss_import_name(minor_status, in_name, in_name_type, out_name); memcpy((char *)&in, (char *)in_name, sizeof(in)); - i = in.length; + if(in.length > CURL_MAX_INPUT_LENGTH) { + if(minor_status) + /* !checksrc! disable ERRNOVAR 1 */ + *minor_status = ENOMEM; + + return GSS_S_FAILURE; + } + i = (unsigned int)in.length; in.value = malloc(i + 1); if(!in.value) { @@ -424,17 +435,17 @@ OM_uint32 Curl_gss_display_status_a(OM_uint32 *minor_status, return rc; } -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_name_t target_name, gss_OID mech_type, - gss_flags_t req_flags, OM_uint32 time_req, - gss_channel_bindings_t input_chan_bindings, - gss_buffer_t input_token, - gss_OID *actual_mech_type, - gss_buffer_t output_token, gss_flags_t *ret_flags, - OM_uint32 *time_rec) +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_name_t target_name, gss_OID mech_type, + gss_flags_t req_flags, OM_uint32 time_req, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_OID *actual_mech_type, + gss_buffer_t output_token, gss_flags_t *ret_flags, + OM_uint32 *time_rec) { int rc; gss_buffer_desc in; @@ -445,8 +456,15 @@ Curl_gss_init_sec_context_a(OM_uint32 *minor_status, if(inp) { if(inp->length && inp->value) { - unsigned int i = inp->length; + unsigned int i; + if(inp->length > CURL_MAX_INPUT_LENGTH) { + if(minor_status) + /* !checksrc! disable ERRNOVAR 1 */ + *minor_status = ENOMEM; + return GSS_S_FAILURE; + } + i = (unsigned int)inp->length; in.value = malloc(i + 1); if(!in.value) { if(minor_status) diff --git a/projects/Windows/generate.bat b/projects/Windows/generate.bat index 8441f7f3f3bf..b5da6d9a798c 100644 --- a/projects/Windows/generate.bat +++ b/projects/Windows/generate.bat @@ -337,8 +337,8 @@ rem :seterr rem Set the caller's errorlevel. rem %1[opt]: Errorlevel as integer. - rem If %1 is empty the errorlevel will be set to 0. - rem If %1 is not empty and not an integer the errorlevel will be set to 1. + rem If %1 is empty the errorlevel is set to 0. + rem If %1 is not empty and not an integer the errorlevel is set to 1. setlocal set EXITCODE=%~1 if not defined EXITCODE set EXITCODE=0 diff --git a/projects/vms/build_gnv_curl_pcsi_desc.com b/projects/vms/build_gnv_curl_pcsi_desc.com index 566384de0257..7726b4aa9f43 100644 --- a/projects/vms/build_gnv_curl_pcsi_desc.com +++ b/projects/vms/build_gnv_curl_pcsi_desc.com @@ -134,7 +134,7 @@ $ write pdsc " end if;" $! $write pdsc " software VMSPORTS ''base' ZLIB ;" $write pdsc - - " if (not ) ;" + " if (not ) ;" $write pdsc " error NEED_ZLIB;" $write pdsc " end if;" $! @@ -425,7 +425,7 @@ $ destname = "[gnv.beta" + destname - "[gnv.usr" $ endif $ endif $! -$! It should be just a directory then. +$! It should be a directory then. $!------------------------------------- $ filedir = f$edit(f$parse(filename,,,"DIRECTORY"), "lowercase") $! If this is not a directory then start processing files. diff --git a/projects/vms/build_vms.com b/projects/vms/build_vms.com index 253fccbc8074..d79fdb594079 100644 --- a/projects/vms/build_vms.com +++ b/projects/vms/build_vms.com @@ -38,7 +38,7 @@ $! Always link a debug image. $! NOIEEE Do not use IEEE floating point. (Alpha/I64) $! VAX must always use DFLOAT $! NOLARGE Disable large-file support if large file support available. -$! (Non-VAX, VMS >= V7.2.) +$! (Non-VAX, VMS >= v7.2.) $! NOLDAP Disable LDAP support if LDAP is available. $! NOKERBEROS Disable Kerberos support if Kerberos is available. $! LIST Create C compiler listings and linker maps. @@ -723,7 +723,7 @@ $ endif $ 'vo_c' " SSLLIB = ''ssllib'" $! $! TODO: Why are we translating the logical name? -$! The logical aname used to find the shared image should just be used +$! The logical aname used to find the shared image should be used $! as translating it could result in the wrong location at run time. $ if (openssl .eq. 1) $ then diff --git a/projects/vms/config_h.com b/projects/vms/config_h.com index e42ecd9d78db..0b23cc6633e5 100644 --- a/projects/vms/config_h.com +++ b/projects/vms/config_h.com @@ -10,7 +10,7 @@ $! The CONFIGURE shell script will be examined for hints and a few symbols $! but most of the tests will not produce valid results on OpenVMS. Some $! will produce false positives and some will produce false negatives. $! -$! It is easier to just read the config.h_in file and make up tests based +$! It is easier to read the config.h_in file and make up tests based $! on what is in it! $! $! This file will create an empty config_vms.h file if one does not exist. diff --git a/projects/vms/curl_gnv_build_steps.txt b/projects/vms/curl_gnv_build_steps.txt index c02c898dad7c..88af34f5802e 100644 --- a/projects/vms/curl_gnv_build_steps.txt +++ b/projects/vms/curl_gnv_build_steps.txt @@ -20,7 +20,7 @@ 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. @@ -268,7 +268,7 @@ branding the PCSI kit based on who is making the kit. This compares the VMS specific source with the backup staging directory for it and updates with any changes. - Leave off "UPDATE" to just check without doing any changes. + Leave off "UPDATE" to check without doing any changes. If you are not using NFS mounted disks and do not want to have a separate directory for staging the sources for backup make sure diff --git a/projects/vms/curl_release_note_start.txt b/projects/vms/curl_release_note_start.txt index 1a67b36020b0..184b458dd669 100644 --- a/projects/vms/curl_release_note_start.txt +++ b/projects/vms/curl_release_note_start.txt @@ -36,7 +36,7 @@ the GNV$LIBCURL shared image and create logical names GNV$LIBCURL to reference it. It will create the GNV$CURL_INCLUDE logical name for build procedures to access the header files. -Normally to use curl from DCL, just create a foreign command as: +Normally to use curl from DCL, create a foreign command as: curl :== $gnv$gnu:[usr.bin]gnv$curl.exe If you need to work around having the older HP SSL kit installed, then diff --git a/projects/vms/curlmsg.msg b/projects/vms/curlmsg.msg index 8d428e88b6b2..b02fe5649c85 100644 --- a/projects/vms/curlmsg.msg +++ b/projects/vms/curlmsg.msg @@ -111,7 +111,7 @@ LDAP_INVALID_URL FILESIZE_EXCEEDED USE_SSL_FAILED SEND_FAIL_REWIND -SSL_ENGINE_INITFAILED +SSL_ENGINE_INITFAILED LOGIN_DENIED TFTP_NOTFOUND TFTP_PERM diff --git a/projects/vms/generate_config_vms_h_curl.com b/projects/vms/generate_config_vms_h_curl.com index e4d97fd566c9..6e78b801f243 100644 --- a/projects/vms/generate_config_vms_h_curl.com +++ b/projects/vms/generate_config_vms_h_curl.com @@ -67,7 +67,7 @@ $if f$locate(",nossl,", args_lower) .lt. args_len then nossl = 1 $if .not. nossl $then $! -$! ssl$* logicals means HP ssl is present +$! ssl$* logicals means HP SSL is present $!---------------------------------------- $ if f$trnlnm("ssl$root") .nes. "" $ then @@ -96,7 +96,7 @@ $ nohpssl = 1 $ hpssl = 0 $ endif $! -$! Finally check to see if hp ssl has been specifically included. +$! Finally check to see if HP SSL has been specifically included. $!---------------------------------------------------------------- $ if f$locate(",nohpssl,", args_lower) .lt. args_len $ then @@ -215,7 +215,7 @@ $write cvh "" $! $! We are now setting this on the GNV build, so also do this $! for compatibility. -$write cvh "/* Location of default ca path */" +$write cvh "/* Location of default CA path */" $write cvh "#define curl_ca_path ""gnv$curl_ca_path""" $! $! The config_h.com finds a bunch of default disable commands in diff --git a/projects/vms/make_gnv_curl_install.sh b/projects/vms/make_gnv_curl_install.sh index 4723070387d0..623fcc3a6be0 100755 --- a/projects/vms/make_gnv_curl_install.sh +++ b/projects/vms/make_gnv_curl_install.sh @@ -37,7 +37,7 @@ export GNV_CC_MAIN_POSIX_EXIT=1 make cd ../.. # adjust the libcurl.pc file, GNV currently ignores the Lib: line. -# but is noisy about it, so we just remove it. +# but is noisy about it, so we remove it. sed -e 's/^Libs:/#Libs:/g' libcurl.pc > libcurl.pc_new rm libcurl.pc mv libcurl.pc_new libcurl.pc diff --git a/projects/vms/make_pcsi_curl_kit_name.com b/projects/vms/make_pcsi_curl_kit_name.com index 956f7c167798..c7c2b26ada7e 100644 --- a/projects/vms/make_pcsi_curl_kit_name.com +++ b/projects/vms/make_pcsi_curl_kit_name.com @@ -67,7 +67,7 @@ $ write sys$output "*****" $! $! $! Base is one of 'VMS', 'AXPVMS', 'I64VMS', 'VAXVMS' and indicates what -$! binaries are in the kit. A kit with just 'VMS' can be installed on all +$! binaries are in the kit. A kit with only 'VMS' can be installed on all $! architectures. $! $ base = "VMS" diff --git a/projects/vms/readme b/projects/vms/readme index 661dc9b471d3..9db0ee38735b 100644 --- a/projects/vms/readme +++ b/projects/vms/readme @@ -19,9 +19,9 @@ curl_gnv_build_steps.txt and other useful information. Prerequisites: -OpenVMS V7.0 or later (any platform) -DECC V6.5 or later -OpenSSL or hp SSL, if you want SSL support +OpenVMS v7.0 or later (any platform) +DECC v6.5 or later +OpenSSL or HP SSL, if you want SSL support What is Here: diff --git a/projects/vms/stage_curl_install.com b/projects/vms/stage_curl_install.com index 10ae17adcb3e..48f6514e5f4e 100644 --- a/projects/vms/stage_curl_install.com +++ b/projects/vms/stage_curl_install.com @@ -96,7 +96,7 @@ $ this_dir = f$element(i, ",", dest_dirs) $ i = i + 1 $ if this_dir .eqs. "" then goto curl_dir_loop $ if this_dir .eqs. "," then goto curl_dir_loop_end -$! Just create the directories, do not delete them. +$! Create the directories, do not delete them. $! -------------------------------------------------- $ if remove_files .eq. 0 $ then diff --git a/scripts/Makefile.am b/scripts/Makefile.am index e0f433422a8d..7ffa98ed9bb6 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -27,7 +27,7 @@ EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl \ 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 + .checksrc badwords badwords-all badwords.txt top-length dist_bin_SCRIPTS = wcurl diff --git a/scripts/badwords b/scripts/badwords index 5ed7a3ece14e..7d371b3beec8 100755 --- a/scripts/badwords +++ b/scripts/badwords @@ -5,7 +5,7 @@ # # bad[:=]correct # -# If separator is '=', the string will be compared case sensitively. +# If separator is '=', the string is 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 @@ -184,14 +184,14 @@ while() { if(/^---:([^:]*):(.*)/) { # whitelist file + word my $word = lc($2); - $wl{"$1:$word"}=1; + $wl{"$1:$word"} = 1; } elsif($_ =~ /^---(.+)/) { # whitelist word push @whitelist, $1; } elsif($_ =~ /^(.*)([:=])(.*)/) { - my ($bad, $sep, $better)=($1, $2, $3); + my ($bad, $sep, $better) = ($1, $2, $3); if($sep eq "=") { $alt{$bad} = $better; push @exact, $bad; @@ -318,7 +318,7 @@ sub file { } my @filemasks = @ARGV; -open(my $git_ls_files, '-|', 'git', 'ls-files', '--', @filemasks) or die "Failed running git ls-files: $!"; +open(my $git_ls_files, '-|', 'git', 'ls-files', '--', ":!:$file", @filemasks) or die "Failed running git ls-files: $!"; my @files; while(my $each = <$git_ls_files>) { chomp $each; diff --git a/scripts/badwords.txt b/scripts/badwords.txt index 2de6f5bbe8f6..2651a4859874 100644 --- a/scripts/badwords.txt +++ b/scripts/badwords.txt @@ -14,13 +14,27 @@ run-time:runtime set-up:setup tool chain:toolchain tool-chain:toolchain +well known:well-known wild-card:wildcard wild card:wildcard +threadsafe:thread-safe thread safe:thread-safe +thread safety:thread-safety thread unsafe:thread-unsafe multi thread:multi-thread +nul terminate:null-terminate +null terminate:null-terminate +zero terminate:null-terminate +nul terminated:null-terminated null terminated:null-terminated +NULL-terminated=null-terminated zero terminated:null-terminated +zero-terminated:null-terminated +nul terminator:null-terminator +null terminator:null-terminator +zero terminator:null-terminator +work-around:workaround or work around +work-arounds:workarounds or works around it's:it is aren't:are not can't:cannot @@ -92,10 +106,13 @@ will:rewrite to present tense 63 bit:63-bit 64 bit:64-bit 128 bit:128-bit +256 bit:256-bit 8-bits:8 bits 16-bits:16 bits 32-bits:32 bits 64-bits:64 bits +initialise:initialize +initialising:initializing very:rephrase using an alternative word just:rephrase using an alternative word simply:rephrase using an alternative word @@ -115,3 +132,4 @@ with with:with ---Curl Corporation ---:lib/:will ---:src/:will +---:tests/data/:file name diff --git a/scripts/cd2cd b/scripts/cd2cd index 182cb62c8a34..ead664dfd66f 100755 --- a/scripts/cd2cd +++ b/scripts/cd2cd @@ -79,7 +79,7 @@ sub outseealso { sub single { my @head; my @seealso; - my ($f)=@_; + my ($f) = @_; my $title; my $section; my $source; @@ -89,7 +89,7 @@ sub single { my $salist = 0; my $copyright; my $spdx; - open(F, "<:crlf", $f) || + open(F, "<:crlf", $f) or return 1; while() { $line++; @@ -103,13 +103,13 @@ sub single { next; } if(/^Title: *(.*)/i) { - $title=$1; + $title = $1; } elsif(/^Section: *(.*)/i) { - $section=$1; + $section = $1; } elsif(/^Source: *(.*)/i) { - $source=$1; + $source = $1; } elsif(/^See-also: +(.*)/i) { $salist = 0; @@ -130,10 +130,10 @@ sub single { } # REUSE-IgnoreStart elsif(/^C: (.*)/i) { - $copyright=$1; + $copyright = $1; } elsif(/^SPDX-License-Identifier: (.*)/i) { - $spdx=$1; + $spdx = $1; } # REUSE-IgnoreEnd elsif(/^---/) { @@ -212,7 +212,7 @@ HEAD close(F); if($inplace) { - open(O, ">$f") || return 1; + open(O, ">$f") or return 1; print O @desc; close(O); } diff --git a/scripts/cd2nroff b/scripts/cd2nroff index 62c9df025a08..27717cfbe31b 100755 --- a/scripts/cd2nroff +++ b/scripts/cd2nroff @@ -100,27 +100,33 @@ sub outprotocols { my (@p) = @_; my $comma = 0; my @o; + my $tls = 0; push @o, ".SH PROTOCOLS\n"; - if($p[0] eq "TLS") { - push @o, "This functionality affects all TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc."; - } - else { - my @s = sort @p; - push @o, "This functionality affects "; - for my $e (sort @s) { + my @s = sort @p; + push @o, "This functionality affects "; + for my $e (sort @s) { + if($e eq "TLS") { + $tls = 1; + } + else { push @o, sprintf "%s%s", $comma ? (($e eq $s[-1]) ? " and " : ", "): "", lc($e); $comma = 1; } - if($#s == 0) { - if($s[0] eq "All") { - push @o, " supported protocols"; - } - else { - push @o, " only"; - } + } + if($tls) { + push @o, sprintf + "%sall TLS based protocols: HTTPS, FTPS, IMAPS, POP3S, SMTPS etc.", + $comma ? " and ": " "; + } + if($#s == 0) { + if($s[0] eq "All") { + push @o, " supported protocols"; + } + else { + push @o, " only"; } } push @o, "\n"; @@ -199,7 +205,7 @@ sub single { my @proto; my @tls; my $d; - my ($f)=@_; + my ($f) = @_; my $copyright; my $errors = 0; my $fh; @@ -234,13 +240,13 @@ sub single { next; } if(/^Title: *(.*)/i) { - $title=$1; + $title = $1; } elsif(/^Section: *(.*)/i) { - $section=$1; + $section = $1; } elsif(/^Source: *(.*)/i) { - $source=$1; + $source = $1; } elsif(/^See-also: +(.*)/i) { $list = 1; # 1 for see-also @@ -260,7 +266,7 @@ sub single { $list = 3; # 3 for TLS backend } elsif(/^Added-in: *(.*)/i) { - $addedin=$1; + $addedin = $1; if(($addedin !~ /^[0-9.]+[0-9]\z/) && ($addedin ne "n/a")) { print STDERR "$f:$line:1:ERROR: invalid version number in Added-in line: $addedin\n"; @@ -285,10 +291,10 @@ sub single { } # REUSE-IgnoreStart elsif(/^C: (.*)/i) { - $copyright=$1; + $copyright = $1; } elsif(/^SPDX-License-Identifier: (.*)/i) { - $spdx=$1; + $spdx = $1; } # REUSE-IgnoreEnd elsif(/^---/) { @@ -426,7 +432,7 @@ sub single { print STDERR "$f:$line:1:ERROR: un-escaped < or > used\n"; $errors++; } - # convert backslash-'<' or '> to just the second character + # convert backslash-'<' or '> to the second character $d =~ s/\\([<>])/$1/g; # mentions of curl symbols with man pages use italics by default diff --git a/scripts/cdall b/scripts/cdall index 1ea0f42f255f..53e6e3692984 100755 --- a/scripts/cdall +++ b/scripts/cdall @@ -29,8 +29,8 @@ use strict; use warnings; sub convert { - my ($dir)=@_; - opendir(my $dh, $dir) || die "could not open $dir"; + my ($dir) = @_; + opendir(my $dh, $dir) or die "could not open $dir"; my @cd = grep { /\.md\z/ && -f "$dir/$_" } readdir($dh); closedir $dh; diff --git a/scripts/checksrc-all.pl b/scripts/checksrc-all.pl index 5b1cba7af624..fd80c1b4277a 100755 --- a/scripts/checksrc-all.pl +++ b/scripts/checksrc-all.pl @@ -13,7 +13,7 @@ my @files; my $is_git = 0; if(system('git rev-parse --is-inside-work-tree >/dev/null 2>&1') == 0) { - open(O, '-|', 'git', 'ls-files', '*.[ch]') || die; push @files, ; close(O); + open(O, '-|', 'git', 'ls-files', '*.[ch]') or die; push @files, ; close(O); $is_git = 1; } else { @@ -34,7 +34,7 @@ for my $dir (@dirs) { if($is_git) { @files = (); - open(O, '-|', 'git', 'ls-files', "$dir/*.[ch]") || die; push @files, ; close(O); + open(O, '-|', 'git', 'ls-files', ":(glob)$dir/*.[ch]") or die; push @files, ; close(O); chomp(@files); } else { diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index d796bc5da0ce..46d02ae3c83a 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -35,8 +35,8 @@ my $serrors = 0; my $suppressed; # skipped problems my $file; -my $dir="."; -my $wlist=""; +my $dir = "."; +my $wlist = ""; my @alist; my $windows_os = $^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys'; my $verbose = 0; @@ -201,14 +201,16 @@ 'TRAILINGSPACE' => 'Trailing whitespace on the line', 'TYPEDEFSTRUCT' => 'typedefed struct', 'UNUSEDIGNORE' => 'a warning ignore was not used', + 'USESAFEFREE' => 'replace curlx_free() + NULL assignment with curlx_safefree()', + 'VOIDEXCL' => '(void)! is not something we like', ); sub readskiplist { open(my $W, '<', "$dir/checksrc.skip") or return; - my @all=<$W>; + my @all = <$W>; for(@all) { $windows_os ? $_ =~ s/\r?\n$// : chomp; - $skiplist{$_}=1; + $skiplist{$_} = 1; } close($W); } @@ -342,7 +344,7 @@ sub checkwarn { } elsif($file =~ /^-b(.*)/) { $banfunc{$1} = $1; - print STDERR "ban use of \"$1\"\n"; + # print STDERR "ban use of \"$1\"\n"; $file = shift @ARGV; next; } @@ -430,8 +432,8 @@ sub accept_violations { print "'$r' is not a warning to accept!\n"; exit; } - $ignore{$r}=999999; - $ignore_used{$r}=0; + $ignore{$r} = 999999; + $ignore_used{$r} = 0; } } @@ -462,9 +464,9 @@ sub enable_warn { $line, length($what) + 11, $file, $l, "No warning was inhibited!"); } - $ignore_set{$what}=0; - $ignore_used{$what}=0; - $ignore{$what}=0; + $ignore_set{$what} = 0; + $ignore_used{$what} = 0; + $ignore{$what} = 0; } sub checksrc { my ($cmd, $line, $file, $l) = @_; @@ -473,9 +475,9 @@ sub checksrc { $what =~ s: *\*/$::; # cut off end of C comment # print "ENABLE $enable WHAT $what\n"; if($enable eq "disable") { - my ($warn, $scope)=($1, $2); + my ($warn, $scope) = ($1, $2); if($what =~ /([^ ]*) +(.*)/) { - ($warn, $scope)=($1, $2); + ($warn, $scope) = ($1, $2); } else { $warn = $what; @@ -483,7 +485,7 @@ sub checksrc { } # print "IGNORE $warn for SCOPE $scope\n"; if($scope eq "all") { - $scope=999999; + $scope = 999999; } # Comparing for a literal zero rather than the scalar value zero @@ -501,9 +503,9 @@ sub checksrc { "$warn already disabled from line $ignore_set{$warn}"); } else { - $ignore{$warn}=$scope; - $ignore_set{$warn}=$line; - $ignore_line[$line]=$l; + $ignore{$warn} = $scope; + $ignore_set{$warn} = $line; + $ignore_line[$line] = $l; } } elsif($enable eq "enable") { @@ -527,20 +529,22 @@ sub scanfile { my ($file) = @_; my $line = 1; - my $prevl=""; - my $prevpl=""; + my $prevl = ""; + my $prevpl = ""; my $l = ""; my $prep = 0; my $prevp = 0; + my $prevfreeindent = ""; + my $prevfreevar = ""; if($verbose) { printf "Checking file: $file\n"; } - open(my $R, '<', $file) || die "failed to open $file"; + open(my $R, '<', $file) or die "failed to open $file"; - my $incomment=0; - my @copyright=(); + my $incomment = 0; + my @copyright = (); my %includes; checksrc_clear(); # for file based ignores accept_violations(); @@ -646,7 +650,7 @@ sub scanfile { } else { # still within a comment - $l=""; + $l = ""; } } @@ -699,7 +703,7 @@ sub scanfile { my $nostr = nostrings($l); # check spaces after for/if/while/function call if($nostr =~ /^(.*)(for|if|while|switch| ([a-zA-Z0-9_]+)) \((.)/) { - my ($leading, $word, $extra, $first)=($1,$2,$3,$4); + my ($leading, $word, $extra, $first) = ($1, $2, $3, $4); if($1 =~ / *\#/) { # this is a #if, treat it differently } @@ -751,7 +755,7 @@ sub scanfile { my $cond = $4; if($cond =~ / = /) { checkwarn("ASSIGNWITHINCONDITION", - $line, $pos+1, $file, $l, + $line, $pos + 1, $file, $l, "assignment within conditional expression"); } my $temp = $cond; @@ -876,7 +880,7 @@ sub scanfile { } } - # check for "return" with parentheses around just a value/name + # check for "return" with parentheses around a value/name if($l =~ /^(.*\W)return \(\w*\);/) { checkwarn("RETURNPAREN", $line, length($1)+7, $file, $l, "return with paren"); @@ -895,15 +899,15 @@ sub scanfile { # check for comma without space if($l =~ /^(.*),[^ \n]/) { - my $pref=$1; - my $ign=0; + my $pref = $1; + my $ign = 0; if($pref =~ / *\#/) { # this is a #if, treat it differently - $ign=1; + $ign = 1; } elsif($pref =~ /\/\*/) { # this is a comment - $ign=1; + $ign = 1; } elsif($pref =~ /[\"\']/) { $ign = 1; @@ -973,6 +977,24 @@ sub scanfile { $line, length($1), $file, $ol, "no space before label"); } + if($prevfreevar ne "") { + if(rindex($l, "$prevfreeindent$prevfreevar = NULL;", 0) == 0) { + checkwarn("USESAFEFREE", + $line, length($prevfreeindent), $file, $ol, + "replace curlx_free() + NULL assignment with curlx_safefree()"); + } + } + if($l) { + if($l =~ /^( *)curlx_free\(([^)]+)\);/) { + $prevfreeindent = $1; + $prevfreevar = $2; + } + else { + $prevfreeindent = ""; + $prevfreevar = ""; + } + } + # scan for use of banned functions my $bl = $l; again: @@ -1144,6 +1166,12 @@ sub scanfile { "space after exclamation mark"); } + if($nostr =~ /(.*)\(void\)\!/) { + checkwarn("VOIDEXCL", + $line, length($1)+1, $file, $ol, + "exclamation after (void) is weird"); + } + if($nostr =~ /(.*)\b(EACCES|EADDRINUSE|EADDRNOTAVAIL|EAFNOSUPPORT|EBADF|ECONNREFUSED|ECONNRESET|EINPROGRESS|EINTR|EINVAL|EISCONN|EMSGSIZE|ENOMEM|ETIMEDOUT|EWOULDBLOCK)\b/) { checkwarn("ERRNOVAR", $line, length($1), $file, $ol, @@ -1199,18 +1227,18 @@ sub scanfile { # A rather more interesting, and correct, check would be to not test # only locally committed files but inspect all files wrt the year of # their last commit. Removing the `git rev-list origin/master..HEAD` - # condition below will enforce copyright year checks against the year + # condition below enforces copyright year checks against the year # the file was last committed (and thus edited to some degree). my $commityear = undef; @copyright = sort {$$b{year} cmp $$a{year}} @copyright; # if the file is modified, assume commit year this year - if(`git status -s -- "$file"` =~ /^ [MARCU]/) { + if(qx(git status -s -- "$file") =~ /^ [MARCU]/) { $commityear = (localtime(time))[5] + 1900; } else { # min-parents=1 to ignore wrong initial commit in truncated repos - my $grl = `git rev-list --max-count=1 --min-parents=1 --timestamp HEAD -- "$file"`; + my $grl = qx(git rev-list --max-count=1 --min-parents=1 --timestamp HEAD -- "$file"); if($grl) { chomp $grl; $commityear = (localtime((split(/ /, $grl))[0]))[5] + 1900; diff --git a/scripts/cmakelint.sh b/scripts/cmakelint.sh index 30a735f87edc..3fe258346df2 100755 --- a/scripts/cmakelint.sh +++ b/scripts/cmakelint.sh @@ -27,7 +27,7 @@ # https://cmake-format.readthedocs.io/en/latest/lint-usage.html # https://github.com/cheshirekow/cmake_format/blob/master/cmakelang/configuration.py -# Run cmakelint on the curl source code. It will check all files given on the +# Run cmakelint on the curl source code. It checks all files given on the # command-line, or else all relevant files in git, or if not in a git # repository, all files starting in the tree rooted in the current directory. # @@ -36,8 +36,8 @@ # # 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'". +# "xargs -I{}" and appending {} to the end of the xargs arguments (which calls +# cmakelint once per file) or by using the GNU extension "xargs -d'\n'". set -eu diff --git a/scripts/completion.pl b/scripts/completion.pl index 27e2f062e7a4..203f2acfb6f4 100755 --- a/scripts/completion.pl +++ b/scripts/completion.pl @@ -80,21 +80,21 @@ sub parse_main_opts { my (@files, @list); my ($dir_handle, $file_content); - opendir($dir_handle, $opts_dir) || die "Unable to open dir: $opts_dir due to error: $!"; + opendir($dir_handle, $opts_dir) or die "Unable to open dir: $opts_dir due to error: $!"; @files = readdir($dir_handle); - closedir($dir_handle) || die "Unable to close handle on dir: $opts_dir due to error: $!"; + closedir($dir_handle) or die "Unable to close handle on dir: $opts_dir due to error: $!"; # 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) { - open(my $doc_handle, '<', "$opts_dir/$file") || die "Unable to open file: $file due to error: $!"; + open(my $doc_handle, '<', "$opts_dir/$file") or die "Unable to open file: $file due to error: $!"; $file_content = join('', <$doc_handle>); - close($doc_handle) || die "Unable to close file: $file due to error: $!"; + close($doc_handle) or die "Unable to close file: $file due to error: $!"; # Extract the curldown header section demarcated by --- - $file_content =~ /^---\s*\n(.*?)\n---\s*\n/s || die "Unable to parse file $file"; + $file_content =~ /^---\s*\n(.*?)\n---\s*\n/s or die "Unable to parse file $file"; $file_content = $1; my ($short, $long, $arg, $desc); diff --git a/scripts/contributors.sh b/scripts/contributors.sh index 37f1e5835bb8..a3a11d52432a 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|^ +[a-z-]+-by):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^ +Credits-to):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ @@ -82,7 +82,7 @@ awk ' { if(length($0)) { num++; - n = sprintf("%s%s%s,", n, length(n)?" ":"", $0); + n = sprintf("%s%s%s,", n, length(n) ? " " : "", $0); #print n; if(length(n) > 77) { printf(" %s\n", p); @@ -93,7 +93,7 @@ awk ' } END { - pp=substr(p,1,length(p)-1); + pp = substr(p, 1, length(p) - 1); printf(" %s\n", pp); printf(" (%d contributors)\n", num); } diff --git a/scripts/contrithanks.sh b/scripts/contrithanks.sh index 47438701cdd4..85f382b755e7 100755 --- a/scripts/contrithanks.sh +++ b/scripts/contrithanks.sh @@ -36,11 +36,11 @@ if test "$start" = "-h"; then exit fi if test "$start" = "stdout"; then - # output the names on stdout - use_stdout="yes" - start="" + # output the names on stdout + use_stdout="yes" + start="" else - use_stdout="no"; + use_stdout="no" fi if test -z "$start"; then start=$(git tag --sort=taggerdate | grep "^curl-" | tail -1) @@ -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|^ +[a-z-]+-by):' | \ + grep -Eai '(^Author|^Commit|^ +[a-z-]+-by|^ +Credits-to):' | \ cut -d: -f2- | \ cut '-d(' -f1 | \ cut '-d<' -f1 | \ diff --git a/scripts/delta b/scripts/delta index 02b0232c47c2..7a1793dbb36a 100755 --- a/scripts/delta +++ b/scripts/delta @@ -34,8 +34,20 @@ use strict; use warnings; use POSIX; +use IPC::Open3; use Time::Piece; +sub cmd { + my @out; + my $as_string = shift if($_[0] eq '$'); # return as string (vs. list of lines) + my $hideerr = shift if($_[0] eq '2>'); # 2>/dev/null + my $pid = open3(my $in, my $out, $hideerr ? '>/dev/null' : '>&STDERR', @_); + close $in; + push @out, <$out>; + waitpid($pid, 0); + return $as_string ? join('', @out) : @out; +} + my $start = $ARGV[0] || ''; if($start eq "-h") { @@ -43,76 +55,92 @@ if($start eq "-h") { exit; } elsif($start eq "") { - $start = `git tag --sort=taggerdate | grep "^curl-" | tail -1`; + my @tags = cmd('git', 'tag', '--sort=taggerdate', '--no-column', '--list', 'curl-*'); + $start = $tags[-1]; # latest tag chomp $start; } -my $commits = `git log --oneline $start.. | wc -l`; -my $committers = `git shortlog -s $start.. | wc -l`; -my $bcommitters = `git shortlog -s $start | wc -l`; +my $commits = cmd('$', 'git', 'rev-list', '--count', "$start.."); +my $committers = cmd('git', 'shortlog', '-s', "$start.."); +my $bcommitters = cmd('git', 'shortlog', '-s', $start); -my $acommits = `git log --oneline | wc -l`; -my $acommitters = `git shortlog -s | wc -l`; +my $acommits = cmd('$', 'git', 'rev-list', '--count', 'HEAD'); +my $acommitters = cmd('git', 'shortlog', '-s', 'HEAD'); # delta from now compared to before my $ncommitters = $acommitters - $bcommitters; # number of contributors right now -my $acontribs = `./scripts/contrithanks.sh stdout | wc -l`; +my $acontribs = cmd('./scripts/contrithanks.sh', 'stdout'); # number when the tag was set -my $bcontribs = `git show $start:docs/THANKS | grep -c '^[^ ]'`; +my $bcontribs = cmd('$', 'git', 'grep', '-h', '-c', '^[^ ]', "$start:docs/THANKS"); # delta my $contribs = $acontribs - $bcontribs; # number of setops: sub setopts { - my ($f)=@_; - open(H, $f); - my $opts; - while() { - if(/^ CURLOPT(|DEPRECATED)\(/ && ($_ !~ /OBSOLETE/)) { - $opts++; + my $mode = shift; + my $opts = 0; + if(open(H, $mode, @_)) { + while() { + if(/^ CURLOPT(|DEPRECATED)\(/ && ($_ !~ /OBSOLETE/)) { + $opts++; + } } + close(H); + } + else { + die join(' ', @_) . ": $!\n"; } - close(H); return $opts; } -my $asetopts = setopts("/dev/null | grep -c '{ *"....--'`; -my $noptions=$aoptions - $boptions; +my $aoptions = cmd('$', '2>', 'git', 'grep', '-h', '-c', '{ *"....--', 'src/tool_listhelp.c'); +my $boptions = cmd('$', '2>', 'git', 'grep', '-h', '-c', '{ *"....--', "$start:src/tool_listhelp.c"); +my $noptions = $aoptions - $boptions; # current local branch -my $branch=`git rev-parse --abbrev-ref HEAD 2>/dev/null`; +my $branch = cmd('$', '2>', 'git', 'rev-parse', '--abbrev-ref', 'HEAD'); chomp $branch; # Number of files in git -my $afiles=`git ls-files | wc -l`; -my $deletes=`git diff-tree --diff-filter=A -r --summary origin/$branch $start 2>/dev/null | wc -l`; -my $creates=`git diff-tree --diff-filter=D -r --summary origin/$branch $start 2>/dev/null | wc -l`; +my $afiles = cmd('git', 'ls-files'); +my $deletes = cmd('2>', 'git', 'diff-tree', '--diff-filter=A', '-r', '--summary', "origin/$branch", $start); +my $creates = cmd('2>', 'git', 'diff-tree', '--diff-filter=D', '-r', '--summary', "origin/$branch", $start); # 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 $taggednice=`git for-each-ref --format="%(refname:short) | %(creatordate)" refs/tags/* | grep ^$start | cut '-d|' -f2`; # human readable time +sub tagstamp { + my $col = shift; + foreach my $line (@_) { + if(index($line, "$start ") == 0) { + my @cols = split(/\|/, $line); + return $cols[$col - 1]; + } + } +} +my @tagged = cmd('git', 'for-each-ref', '--format=%(refname:short) | %(taggerdate:unix) | %(creatordate)', 'refs/tags/*'); +my $tagged = tagstamp(2, @tagged); # Unix timestamp +my $taggednice = tagstamp(3, @tagged); # human readable time chomp $taggednice; -my $now=POSIX::strftime("%s", localtime()); -my $elapsed=$now - $tagged; # number of seconds since tag -my $total=$now - Time::Piece->strptime('19980320', '%Y%m%d')->epoch; -my $totalhttpget=$now - Time::Piece->strptime('19961111', '%Y%m%d')->epoch; +my $now = POSIX::strftime("%s", localtime()); +my $elapsed = $now - $tagged; # number of seconds since tag +my $total = $now - Time::Piece->strptime('19980320', '%Y%m%d')->epoch; +my $totalhttpget = $now - Time::Piece->strptime('19961111', '%Y%m%d')->epoch; # Number of public functions in libcurl -my $apublic=`git grep ^CURL_EXTERN -- include/curl | wc -l`; -my $bpublic=`git grep ^CURL_EXTERN $start -- include/curl | wc -l`; +my $apublic = cmd('git', 'grep', '^CURL_EXTERN', '--', 'include/curl'); +my $bpublic = cmd('git', 'grep', '^CURL_EXTERN', $start, '--', 'include/curl'); my $public = $apublic - $bpublic; # diffstat my ($fileschanged, $insertions, $deletions); -my $diffstat=`git diff --stat $start.. | tail -1`; +my @diffstat = cmd('2>', 'git', 'diff', '--stat', "$start.."); +my $diffstat = $diffstat[-1]; if($diffstat =~ /^ *(\d+) files changed, (\d+) insertions\(\+\), (\d+)/) { - ($fileschanged, $insertions, $deletions)=($1, $2, $3); + ($fileschanged, $insertions, $deletions) = ($1, $2, $3); } # Changes/bug-fixes currently logged @@ -123,16 +151,16 @@ open(F, ") { if($_ =~ /following changes:/) { - $mode=1; + $mode = 1; } elsif($_ =~ /following bugfixes:/) { - $mode=2; + $mode = 2; } elsif($_ =~ /known bugs:/) { - $mode=3; + $mode = 3; } elsif($_ =~ /like these:/) { - $mode=4; + $mode = 4; } if($_ =~ /^ o /) { if($mode == 1) { diff --git a/scripts/extract-unit-protos b/scripts/extract-unit-protos index b9154012f4ff..79c66057948d 100755 --- a/scripts/extract-unit-protos +++ b/scripts/extract-unit-protos @@ -35,7 +35,7 @@ my $error; sub scanfile { my ($file) = @_; - open(F, "<$file") || die "$file failed"; + open(F, "<$file") or die "$file failed"; my $unit = 0; my $line = 0; my $unitref = 0; diff --git a/scripts/firefox-db2pem.sh b/scripts/firefox-db2pem.sh index 7d31b1288693..b763ef10a26a 100755 --- a/scripts/firefox-db2pem.sh +++ b/scripts/firefox-db2pem.sh @@ -23,7 +23,7 @@ # * # *************************************************************************** # This shell script creates a fresh ca-bundle.crt file for use with libcurl. -# It extracts all ca certs it finds in the local Firefox database and converts +# It extracts all CA certs it finds in the local Firefox database and converts # them all into PEM format. # # It uses the "certutil" command line tool from the NSS project to perform the diff --git a/scripts/managen b/scripts/managen index d70df2fbfd0c..2bf1c3d75ded 100755 --- a/scripts/managen +++ b/scripts/managen @@ -66,7 +66,7 @@ my $indent = 4; # get the long name version, return the man page string sub manpageify { - my ($k, $manpage)=@_; + my ($k, $manpage) = @_; my $trail = ''; # the matching pattern might include a trailing dot that cannot be part of # the option name @@ -85,12 +85,12 @@ sub manpageify { return "--$k$trail"; } -my $colwidth=79; # max number of columns +my $colwidth = 79; # max number of columns sub prefixline { my ($num) = @_; - print "\t" x ($num/8); - print ' ' x ($num%8); + print "\t" x ($num / 8); + print ' ' x ($num % 8); } sub justline { @@ -220,7 +220,7 @@ sub printdesc { } sub seealso { - my($standalone, $data)=@_; + my($standalone, $data) = @_; if($standalone) { return sprintf ".SH \"SEE ALSO\"\n$data\n"; @@ -231,7 +231,7 @@ sub seealso { } sub overrides { - my ($standalone, $data)=@_; + my ($standalone, $data) = @_; if($standalone) { return ".SH \"OVERRIDES\"\n$data\n"; } @@ -263,7 +263,7 @@ my %protexists = ( ); sub protocols { - my ($f, $line, $manpage, $standalone, $data)=@_; + my ($f, $line, $manpage, $standalone, $data) = @_; my @e = split(/ +/, $data); for my $pr (@e) { if(!$protexists{$pr}) { @@ -282,7 +282,7 @@ sub protocols { } sub too_old { - my ($version)=@_; + my ($version) = @_; my $a = 999999; if($version =~ /^(\d+)\.(\d+)\.(\d+)/) { $a = $1 * 1000 + $2 * 10 + $3; @@ -299,7 +299,7 @@ sub too_old { } sub added { - my ($standalone, $data)=@_; + my ($standalone, $data) = @_; if(too_old($data)) { # do not mention ancient additions return ""; @@ -494,7 +494,7 @@ sub render { } } - # convert backslash-'<' or '> to just the second character + # convert backslash-'<' or '> to the second character $d =~ s/\\([><])/$1/g; # convert single backslash to double-backslash $d =~ s/\\/\\\\/g if($manpage); @@ -560,9 +560,9 @@ sub maybespace { } sub single { - my ($dir, $manpage, $f, $standalone)=@_; + my ($dir, $manpage, $f, $standalone) = @_; my $fh; - open($fh, "<:crlf", "$dir/$f") || + open($fh, "<:crlf", "$dir/$f") or die "could not find $dir/$f"; my $short; my $long; @@ -597,28 +597,28 @@ sub single { next; } if(/^Short: *(.)/i) { - $short=$1; + $short = $1; } elsif(/^Long: *(.*)/i) { - $long=$1; + $long = $1; } elsif(/^Added: *(.*)/i) { - $added=$1; + $added = $1; } elsif(/^Tags: *(.*)/i) { - $tags=$1; + $tags = $1; } elsif(/^Arg: *(.*)/i) { - $arg=$1; + $arg = $1; } elsif(/^Magic: *(.*)/i) { - $magic=$1; + $magic = $1; } elsif(/^Mutexed: *(.*)/i) { - $mutexed=$1; + $mutexed = $1; } elsif(/^Protocols: *(.*)/i) { - $protocols=$1; + $protocols = $1; } elsif(/^See-also: +(.+)/i) { if(@seealso) { @@ -628,16 +628,16 @@ sub single { push @seealso, $1; } elsif(/^See-also:/i) { - $list=2; + $list = 2; } elsif(/^ *- (.*)/i && ($list == 2)) { push @seealso, $1; } elsif(/^Requires: *(.*)/i) { - $requires=$1; + $requires = $1; } elsif(/^Category: *(.*)/i) { - $category=$1; + $category = $1; } elsif(/^Example: +(.+)/i) { push @examples, $1; @@ -650,20 +650,20 @@ sub single { push @examples, $1; } elsif(/^Multi: *(.*)/i) { - $multi=$1; + $multi = $1; } elsif(/^Scope: *(.*)/i) { - $scope=$1; + $scope = $1; } elsif(/^Experimental: yes/i) { - $experimental=1; + $experimental = 1; } # REUSE-IgnoreStart elsif(/^C: (.*)/i) { - $copyright=$1; + $copyright = $1; } elsif(/^SPDX-License-Identifier: (.*)/i) { - $spdx=$1; + $spdx = $1; } # REUSE-IgnoreEnd elsif(/^Help: *(.*)/i) { @@ -705,7 +705,7 @@ sub single { } else { chomp; - print STDERR "$f:$line:1:WARN: unrecognized line in $f, ignoring:\n:'$_';" + print STDERR "$f:$line:1:WARN: unrecognized line in $f, ignoring: '$_';\n"; } } @@ -787,7 +787,7 @@ sub single { } elsif($multi eq "append") { push @extra, - sprintf("${pre}%s can be used several times in a command line\n", + sprintf("${pre}%s can be used several times in a command line.\n", manpageify($long, $manpage)); } elsif($multi eq "boolean") { @@ -855,7 +855,7 @@ sub single { " is built to support $requires.\n"; } if($mutexed) { - my @m=split(/ /, $mutexed); + my @m = split(/ /, $mutexed); my $mstr; my $num = scalar(@m); my $count = 0; @@ -868,15 +868,15 @@ sub single { if($count == ($num -1)) { $sep = " and "; } - $mstr .= sprintf "%s$l", $mstr?$sep:""; + $mstr .= sprintf "%s$l", $mstr ? $sep : ""; $count++; } push @foot, overrides($standalone, "This option is mutually exclusive with $mstr.\n"); } if($examples[0]) { - my $s =""; - $s="s" if($examples[1]); + my $s = ""; + $s = "s" if($examples[1]); foreach my $e (@examples) { my $check = $e; # verify the used options @@ -916,7 +916,7 @@ sub single { push @ex, "[0q]Example$s:\n"; # # long ASCII examples are wrapped. Preferably at the last space - # before the margin. Or at a colon. Otherwise it just cuts at the + # before the margin. Or at a colon. Otherwise it cuts at the # exact boundary. # foreach my $e (@examples) { @@ -933,7 +933,7 @@ sub single { if(length($e) > $maxwidth) { $r = maybespace($r); } - my $slash =""; + my $slash = ""; $e = substr($e, length($r)); if(length($e) > 0) { $slash = "\\"; @@ -967,9 +967,9 @@ sub single { } sub getshortlong { - my ($dir, $f)=@_; + my ($dir, $f) = @_; $f =~ s/^.*\///; - open(F, "<:crlf", "$dir/$f") || + open(F, "<:crlf", "$dir/$f") or die "could not find $dir/$f"; my $short; my $long; @@ -988,13 +988,13 @@ sub getshortlong { next; } if(/^Short: (.)/i) { - $short=$1; + $short = $1; } elsif(/^Long: (.*)/i) { - $long=$1; + $long = $1; } elsif(/^Help: (.*)/i) { - $help=$1; + $help = $1; my $len = length($help); if($len >= 49) { printf STDERR "$f:$line:1:WARN: oversized help text: %d characters\n", @@ -1002,13 +1002,13 @@ sub getshortlong { } } elsif(/^Arg: (.*)/i) { - $arg=$1; + $arg = $1; } elsif(/^Protocols: (.*)/i) { - $protocols=$1; + $protocols = $1; } elsif(/^Category: (.*)/i) { - $category=$1; + $category = $1; } elsif(/^---/) { last; @@ -1016,14 +1016,14 @@ sub getshortlong { } close(F); if($short) { - $optshort{$short}=$long; + $optshort{$short} = $long; } if($long) { - $optlong{$long}=$short; - $helplong{$long}=$help; - $arglong{$long}=$arg; - $protolong{$long}=$protocols; - $catlong{$long}=$category; + $optlong{$long} = $short; + $helplong{$long} = $help; + $arglong{$long} = $arg; + $protolong{$long} = $protocols; + $catlong{$long} = $category; } } @@ -1035,9 +1035,9 @@ sub indexoptions { } sub header { - my ($dir, $manpage, $f)=@_; + my ($dir, $manpage, $f) = @_; my $fh; - open($fh, "<:crlf", "$dir/$f") || + open($fh, "<:crlf", "$dir/$f") or die "could not find $dir/$f"; my @d = render($manpage, $fh, $f, 1); close($fh); @@ -1047,7 +1047,7 @@ sub header { sub sourcecategories { my ($dir) = @_; my %cats; - open(H, "<$dir/../../src/tool_help.h") || + open(H, "<$dir/../../src/tool_help.h") or die "cannot find the header file"; while() { if(/^\#define CURLHELP_([A-Z0-9]*)/) { @@ -1177,7 +1177,7 @@ sub listglobals { # Find all global options and output them foreach my $f (sort @files) { - open(F, "<:crlf", "$dir/$f") || + open(F, "<:crlf", "$dir/$f") or die "could not read $dir/$f"; my $long; my $start = 0; @@ -1192,7 +1192,7 @@ sub listglobals { } } if(/^Long: *(.*)/i) { - $long=$1; + $long = $1; } elsif(/^Scope: global/i) { push @globalopts, $long; @@ -1202,7 +1202,7 @@ sub listglobals { close(F); } for my $e (0 .. $#globalopts) { - $globals .= sprintf "%s--%s", $e?($globalopts[$e+1] ? ", " : " and "):"", + $globals .= sprintf "%s--%s", $e ? ($globalopts[$e + 1] ? ", " : " and ") : "", $globalopts[$e],; } } @@ -1222,7 +1222,7 @@ sub mainpage { # $manpage is 1 for nroff, 0 for ASCII my $ret; my $fh; - open($fh, "<:crlf", "$dir/mainpage.idx") || + open($fh, "<:crlf", "$dir/mainpage.idx") or die "no $dir/mainpage.idx file"; print <
) { if($_ =~ /^#define LIBCURL_VERSION \"([0-9.]*)/) { $version = $1; diff --git a/scripts/mdlinkcheck b/scripts/mdlinkcheck index 4569b764d789..180203c41e41 100755 --- a/scripts/mdlinkcheck +++ b/scripts/mdlinkcheck @@ -90,7 +90,7 @@ if(defined $ARGV[0] && $ARGV[0] eq "--dry-run") { } # list all files to scan for links -my @files=`git ls-files docs include lib scripts src`; +my @files = qx(git ls-files docs include lib scripts src); sub storelink { my ($f, $line, $link) = @_; @@ -108,7 +108,7 @@ sub storelink { #print "-- whitelisted: $link\n"; $whitelist{$link}++; } - # example.com is just example + # example.com is used as example elsif($link =~ /^https:\/\/(.*)example.(com|org|net)/) { #print "-- example: $link\n"; } @@ -154,7 +154,7 @@ sub storelink { sub findlinks { my ($f) = @_; my $line = 1; - open(F, "<:crlf", $f) || + open(F, "<:crlf", $f) or return; # is it a markdown extension? diff --git a/scripts/mk-ca-bundle.pl b/scripts/mk-ca-bundle.pl index 4eb759863435..c83f2ba5e3c9 100755 --- a/scripts/mk-ca-bundle.pl +++ b/scripts/mk-ca-bundle.pl @@ -153,7 +153,7 @@ () 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 " 6. Use of this script makes a security engineer grind his teeth and\n"; print " swear at you. ;)\n"; exit; } else { # Short Form Warning @@ -228,7 +228,7 @@ ($$@) if(scalar(@invalid) > 0) { # Tell the user which parameters were invalid and print the standard help - # message which will exit + # message which also exits print "Error: Invalid ", $description, scalar(@invalid) == 1 ? ": " : "s: ", join(", ", map { "\"$_\"" } @invalid), "\n"; HELP_MESSAGE(); } @@ -257,7 +257,7 @@ sub sha256 { sub oldhash { my $hash = ""; - open(C, "<$_[0]") || return 0; + open(C, "<$_[0]") or return 0; while() { chomp; if($_ =~ /^\#\# SHA256: (.*)/) { @@ -309,7 +309,7 @@ (%) # If we have an HTTPS URL then use curl if($url =~ /^https:\/\//i) { - my $curl = `curl -V`; + my $curl = qx(curl -V); if($curl) { if($curl =~ /^Protocols:.* https( |$)/m) { report "Get certdata with curl!"; @@ -421,7 +421,7 @@ (%) ## It contains the certificates in ${format}PEM format and therefore ## can be directly used with curl / libcurl / php_curl, or with ## an Apache+mod_ssl webserver for SSL client authentication. -## Just configure this file as the SSLCACertificateFile. +## Configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version $version. ## SHA256: $newhash @@ -493,7 +493,7 @@ (%) # # The latter is for certificates that have already been removed and are not # included. Not all explicitly distrusted certificates are ignored at this - # point, just those without an actual certificate. + # point, only those without an actual certificate. elsif(!$main_block && !$trust_block) { next; } diff --git a/scripts/mk-unity.pl b/scripts/mk-unity.pl index 014cfe8eceb9..41354c15d120 100755 --- a/scripts/mk-unity.pl +++ b/scripts/mk-unity.pl @@ -65,7 +65,7 @@ sub include($@) { my $filename = shift; - if($concat) { + if($concat && $filename =~ /([a-z0-9_]+)\.c$/) { if(! -f $filename) { foreach my $path (@incpath) { my $fullfn = $path . "/" . $filename; diff --git a/scripts/nroff2cd b/scripts/nroff2cd index 51b6974b8dfb..374b0cbdab58 100755 --- a/scripts/nroff2cd +++ b/scripts/nroff2cd @@ -30,7 +30,7 @@ This script converts an nroff file to curldown Example: cd2nroff [options] > Note: when converting .nf sections, this tool does not know if the -section is code or just regular quotes. It then assumes and uses ~~~c +section is code or regular quotes. It then assumes and uses ~~~c for code. =end comment @@ -42,8 +42,8 @@ use warnings; my $nroff2cd = "0.1"; # to keep check sub single { - my ($f)=@_; - open(F, "<:crlf", $f) || + my ($f) = @_; + open(F, "<:crlf", $f) or 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) or return 1; } if($d =~ /^\.TH ([^ ]*) (\d) \"(.*?)\" ([^ \n]*)/) { # header, this needs to be the first thing after leading comments @@ -116,7 +116,7 @@ HEAD # if there are enclosing quotes, remove them first $word =~ s/[\"\'](.*)[\"\']\z/$1/; if($word eq "SEE ALSO") { - # we just slurp up this section + # we slurp up this section next; } push @desc, "\n# $word\n\n"; @@ -131,7 +131,7 @@ HEAD push @desc, "\n## $word\n\n"; } elsif($d =~ /^\.IP/) { - # .IP with no text we just skip + # .IP with no text we skip } elsif($d =~ /^\.BR (.*)/) { # only used for SEE ALSO diff --git a/scripts/perlcheck.sh b/scripts/perlcheck.sh index c243f50271c5..28c5be2c1678 100755 --- a/scripts/perlcheck.sh +++ b/scripts/perlcheck.sh @@ -25,8 +25,8 @@ # 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'". +# "xargs -I{}" and appending {} to the end of the xargs arguments (which calls +# cmakelint once per file) or by using the GNU extension "xargs -d'\n'". set -eu diff --git a/scripts/pythonlint.sh b/scripts/pythonlint.sh index 5c1a3cdcc3fd..a6d6aa2dd4ce 100755 --- a/scripts/pythonlint.sh +++ b/scripts/pythonlint.sh @@ -27,6 +27,8 @@ # locations, or all Python files found in the current directory tree by # default. +set -eu + 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 \ diff --git a/scripts/randdisable b/scripts/randdisable index a59c6c812563..f91a2ac3b28c 100755 --- a/scripts/randdisable +++ b/scripts/randdisable @@ -28,7 +28,7 @@ use List::Util qw/shuffle/; my @disable; sub getoptions { - my @all = `./configure --help`; + my @all = qx(./configure --help); for my $o (@all) { chomp $o; if($o =~ /(--disable-[^ ]*)/) { diff --git a/scripts/release-notes.pl b/scripts/release-notes.pl index a4b4e2550fa0..c26c5037e8ea 100755 --- a/scripts/release-notes.pl +++ b/scripts/release-notes.pl @@ -32,8 +32,8 @@ # $ ./scripts/release-notes.pl # # 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 +# references below are cleaned up in the next step. Make sure to move +# "changes" up to the changes section. All entries are by default listed # 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 @@ -58,8 +58,8 @@ use warnings; my $cleanup = (@ARGV && $ARGV[0] eq "cleanup"); -my @gitlog=`git log @^{/RELEASE-NOTES:.synced}..` if(!$cleanup); -my @releasenotes=`cat RELEASE-NOTES`; +my @gitlog = qx(git log @^{/RELEASE-NOTES:.synced}..) if(!$cleanup); +my @releasenotes = qx(cat RELEASE-NOTES); my @o; # the entire new RELEASE-NOTES my @refused; # [num] = [2 bits of use info] @@ -68,7 +68,7 @@ for my $l (@releasenotes) { if($l =~ /^ o .*\[(\d+)\]/) { # referenced, set bit 0 - $refused[$1]=1; + $refused[$1] = 1; my $m = $l; chomp $m; $m =~ s/^ o //; @@ -107,7 +107,7 @@ sub getref { # 'https://elsewhere.example.com/discussion' sub extract { - my ($ref)=@_; + my ($ref) = @_; if($ref =~ /^(\#|)(\d+)/) { # return the plain number return $2; @@ -175,7 +175,7 @@ sub extract { # call at the end of a parsed commit sub onecommit { - my ($short)=@_; + my ($short) = @_; my $ref = ''; if($dupe{$short}) { @@ -199,7 +199,7 @@ sub onecommit { if($ref) { my $r = getref(); $refs[$r] = $ref; - $moreinfo{$short}=$r; + $moreinfo{$short} = $r; $refused[$r] |= 1; } } @@ -220,7 +220,7 @@ sub onecommit { push @o, sprintf " o %s%s\n", $f, $moreinfo{$f}? sprintf(" [%d]", $moreinfo{$f}): ""; if($moreinfo{$f}) { - $refused[$moreinfo{$f}]=3; + $refused[$moreinfo{$f}] = 3; } } push @o, " --- new entries are listed above this ---"; diff --git a/scripts/schemetable.c b/scripts/schemetable.c index afa54c39aff7..8127ecd2a16a 100644 --- a/scripts/schemetable.c +++ b/scripts/schemetable.c @@ -53,6 +53,11 @@ static const char *scheme[] = { "smbs", "smtp", "smtps", + "socks", + "socks4", + "socks4a", + "socks5", + "socks5h", "telnet", "tftp", "ws", diff --git a/scripts/singleuse.pl b/scripts/singleuse.pl index 957334215078..fc6cc7f1116c 100755 --- a/scripts/singleuse.pl +++ b/scripts/singleuse.pl @@ -182,7 +182,7 @@ sub doublecheck { return @also; } -open(N, '-|', 'nm', $file) || die; +open(N, '-|', 'nm', $file) or die; my %exist; my %uses; @@ -198,13 +198,13 @@ sub doublecheck { $file = $1; } if($l =~ /^([0-9a-f]+) T _?(.*)/) { - my ($name)=($2); + my ($name) = ($2); #print "Define $name in $file\n"; $file =~ s/^libcurl_la-//; $exist{$name} = $file; } elsif($l =~ /^ U _?(.*)/) { - my ($name)=($1); + my ($name) = ($1); #print "Uses $name in $file\n"; $uses{$name} .= "$file, "; } diff --git a/scripts/top-complexity b/scripts/top-complexity index f2d869f607a2..7ad84f29dd9b 100755 --- a/scripts/top-complexity +++ b/scripts/top-complexity @@ -30,15 +30,15 @@ use warnings; # Check for a command in the PATH of the test server. # sub checkcmd { - my ($cmd)=@_; + my ($cmd) = @_; my @paths; if($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') { # PATH separator is different - @paths=(split(';', $ENV{'PATH'})); + @paths = (split(';', $ENV{'PATH'})); } else { - @paths=(split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", - "/sbin", "/usr/bin", "/usr/local/bin"); + @paths = (split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin"); } for(@paths) { if(-x "$_/$cmd" && ! -d "$_/$cmd") { @@ -82,7 +82,7 @@ my %whitelist = ( # complexity above this level is treated as an error and contributes to the # script's exit code -my $cutoff = 60; +my $cutoff = 50; # show this many from the top my $top = $ARGV[0] ? $ARGV[0] : 25; @@ -96,16 +96,16 @@ my $alllines = 0; # 142 417 809 1677 1305 src/tool_getparam.c(1677): getparameter for my $l (@output) { chomp $l; - if($l =~/^(\d+)\t\d+\t\d+\t\d+\t(\d+)\t([^\(]+).*: ([^ ]*)/) { - my ($score, $len, $path, $func)=($1, $2, $3, $4); + if($l =~ /^(\d+)\t\d+\t\d+\t\d+\t(\d+)\t([^\(]+).*: ([^ ]*)/) { + my ($score, $len, $path, $func) = ($1, $2, $3, $4); my $allow = 0; if($whitelist{$func} && ($score <= $whitelist{$func})) { $allow = 1; } - $where{"$path:$func"}=$score; - $perm{"$path:$func"}=$allow; + $where{"$path:$func"} = $score; + $perm{"$path:$func"} = $allow; if(($score > $cutoff) && !$allow) { $error++; } diff --git a/scripts/top-length b/scripts/top-length new file mode 100755 index 000000000000..7bed39b8db24 --- /dev/null +++ b/scripts/top-length @@ -0,0 +1,133 @@ +#!/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; + +####################################################################### +# Check for a command in the PATH of the test server. +# +sub checkcmd { + my ($cmd) = @_; + my @paths; + if($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') { + # PATH separator is different + @paths = (split(';', $ENV{'PATH'})); + } + else { + @paths = (split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin"); + } + for(@paths) { + if(-x "$_/$cmd" && ! -d "$_/$cmd") { + # executable bit but not a directory! + return "$_/$cmd"; + } + } + return ""; +} + +my $pmccabe = checkcmd("pmccabe"); +if(!$pmccabe) { + print "Make sure 'pmccabe' exists in your PATH\n"; + exit 1; +} +if(! -r "lib/url.c" || ! -r "lib/urldata.h") { + print "Invoke this script in the curl source tree root\n"; + exit 1; +} + +my @files; +open(my $git, "-|", "git", "ls-files", "*.c") or die "git ls-files failed: $!"; +while(<$git>) { + chomp $_; + my $file = $_; + # we cannot filter these with git so do it here + if($file =~ /^(lib|src)/) { + push @files, $file; + } +} +close($git); + +open(my $pmc, "-|", $pmccabe, @files) or die "pmccabe failed: $!"; +my @output = <$pmc>; +close($pmc); + +# these functions can be this long, but not longer +my %whitelist = ( + ); + +# function length above this level is treated as an error and contributes to +# the script's exit code +my $cutoff = 500; + +# show this many from the top +my $top = $ARGV[0] ? $ARGV[0] : 25; + +my $error = 0; +my %where; +my %perm; +my $funcs = 0; +my $alllines = 0; +# each line starts with the complexity score +# 142 417 809 1677 1305 src/tool_getparam.c(1677): getparameter +for my $l (@output) { + chomp $l; + if($l =~ /^(\d+)\t\d+\t\d+\t\d+\t(\d+)\t([^\(]+).*: ([^ ]*)/) { + my ($score, $length, $path, $func) = ($1, $2, $3, $4); + + my $allow = 0; + if($whitelist{$func} && + ($length <= $whitelist{$func})) { + $allow = 1; + } + $where{"$path:$func"} = $length; + $perm{"$path:$func"} = $allow; + if(($length > $cutoff) && !$allow) { + $error++; + } + + $alllines += $length; + $funcs++; + } +} + +my $showncutoff; +for my $e (sort {$where{$b} <=> $where{$a}} keys %where) { + if(!$showncutoff && + ($where{$e} <= $cutoff)) { + print "\n---- threshold: $cutoff ----\n\n"; + $showncutoff = 1; + } + printf "%-5d %s%s\n", $where{$e}, $e, + $perm{$e} ? " [ALLOWED]" : ""; + if(!--$top) { + last; + } +} + +printf "\nAverage function length: %.2f lines\n", $alllines/$funcs; + +exit $error; diff --git a/scripts/verify-release b/scripts/verify-release index b24d9b370d5a..fd7aa3ded6be 100755 --- a/scripts/verify-release +++ b/scripts/verify-release @@ -27,8 +27,10 @@ # This script remakes a provided curl release and verifies that the newly # built version is identical to the original file. # -# It is designed to be invoked in a clean directory with the path to the -# release tarball as an argument. +# Invoke in a clean directory with the release tarball file (stored in the +# same directory) as an argument for basic verification. +# +# For full verification: run the script in an up-to-date curl git repository. # set -eu @@ -36,15 +38,25 @@ set -eu tarball="${1:-}" if [ -z "$tarball" ]; then - echo "Provide a curl release tarball name as argument" - exit + echo "Provide a curl release tarball name as argument" + exit fi i="$(find . -maxdepth 1 -type d -name 'curl-*' | wc -l)" if test "$i" -gt 1; then - echo "multiple curl-* entries found, disambiguate please" - exit + echo "multiple curl-* entries found, disambiguate please" + exit +fi + +# check if this is in a git clone directory + +if git log -1 include/curl/curl.h 2>/dev/null >/dev/null; then + echo "*** Detected a git checkout, do full verification" + withgit=1 +else + echo "*** Lacking a full git checkout, do the lesser verification" + withgit=0 fi mkdir -p _tarballs @@ -53,22 +65,56 @@ rm -rf _tarballs/* # checksum the original tarball to compare with later sha256sum "$tarball" >_tarballs/checksum -# extract the release contents -tar xf "$tarball" +# extract version number from file name +tarver=$(echo "$tarball" | sed 's/curl-\([0-9.]*\)\..*/\1/') + +# extract the version from the official header file +curlver=$(tar xOf "$tarball" "curl-$tarver/include/curl/curlver.h" | grep '#define LIBCURL_VERSION ' | sed 's/[^0-9.]//g') + +if test "$tarver" != "$curlver"; then + echo "Tarball file version ($tarver) mismatches contents of tarball ($curlver)" + exit 1 +fi + +timestamp=$(tar xOf "$tarball" "curl-$tarver/docs/RELEASE-TOOLS.md" | grep 'SOURCE_DATE_EPOCH=' | sed 's/[^0-9.]//g') + +if test "$withgit" = 0; then + # without git + + # extract the release contents + tar xf "$tarball" + + # move away the original tarball + mv "$tarball" "_tarballs/orig-$tarball" -curlver=$(grep '#define LIBCURL_VERSION ' curl-*/include/curl/curlver.h | sed 's/[^0-9.]//g') + pwd=$(pwd) + cd "curl-$curlver" + ./configure --without-ssl --without-libpsl + ./scripts/dmaketgz "$curlver" "$timestamp" -echo "version $curlver" + for f in "curl-$curlver.tar.gz" "curl-$curlver.tar.bz2" "curl-$curlver.tar.xz" "curl-$curlver.zip"; do + mv "$f" ../_tarballs/ + done + cd "$pwd" +else + tag=$(tar xOf "$tarball" "curl-$tarver/docs/RELEASE-TOOLS.md" | grep 'tag/commit: curl-' | head -n 1 | sed 's/.*\(curl-[0-9_]*\).*/\1/') + echo "*** Use git tag $tag" -timestamp=$(grep -Eo 'SOURCE_DATE_EPOCH=[0-9]*' curl-"$curlver"/docs/RELEASE-TOOLS.md | cut -d= -f2) + # move away the original tarball + mv "$tarball" "_tarballs/orig-$tarball" -pwd=$(pwd) -cd "curl-$curlver" -./configure --without-ssl --without-libpsl -./scripts/dmaketgz "$curlver" "$timestamp" + prevtag=$(git symbolic-ref -q --short HEAD || git rev-parse HEAD) + git checkout -f "$tag" -mv curl-"$curlver"* ../_tarballs/ -cd "$pwd" + ./scripts/dmaketgz "$curlver" "$timestamp" + + # switch back to where it was + git checkout -f "$prevtag" + + for f in "curl-$curlver.tar.gz" "curl-$curlver.tar.bz2" "curl-$curlver.tar.xz" "curl-$curlver.zip"; do + mv "$f" _tarballs/ + done +fi cd "_tarballs" # compare the new tarball against the original diff --git a/scripts/wcurl b/scripts/wcurl index c39806cf946d..a5c1f95e1cc4 100755 --- a/scripts/wcurl +++ b/scripts/wcurl @@ -67,7 +67,7 @@ Options: --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: Do not actually execute curl, just print what would be invoked. + --dry-run: Do not actually execute curl, only print what would be invoked. -V, --version: Print version information. @@ -197,7 +197,7 @@ get_url_filename() # 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. + # No slash means there was only a hostname and no path; return empty string. } # Execute curl with the list of URLs provided by the user. diff --git a/src/Makefile.inc b/src/Makefile.inc index fe2bae5c32e3..84c0a3a02de7 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -24,7 +24,7 @@ # Shared between CMakeLists.txt and Makefile.am # Using the backslash as line continuation character might be problematic with -# some make flavours. If we ever want to change this in a portable manner then +# some make flavors. If we ever want to change this in a portable manner then # we should consider this idea : # CSRC1 = file1.c file2.c file3.c # CSRC2 = file4.c file5.c file6.c diff --git a/src/config2setopts.c b/src/config2setopts.c index 9138b3b14742..8933668a2c7e 100644 --- a/src/config2setopts.c +++ b/src/config2setopts.c @@ -147,29 +147,40 @@ static CURLcode url_proto_and_rewrite(char **url, 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; -#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; + if(config->proto_default) { + /* when a default proto is requested, do not guess */ + uc = curl_url_get(uh, CURLUPART_SCHEME, &schemep, + CURLU_NO_GUESS_SCHEME); + if(uc == CURLUE_NO_SCHEME) { + /* use the default */ + proto = proto_token(config->proto_default); + if(proto) + uc = CURLUE_OK; } - else -#endif /* !CURL_DISABLE_IPFS */ - proto = proto_token(schemep); - curl_free(schemep); } - else if(uc == CURLUE_OUT_OF_MEMORY) + else { + uc = curl_url_get(uh, CURLUPART_SCHEME, &schemep, + CURLU_DEFAULT_SCHEME); + } + if(schemep) + proto = proto_token(schemep); +#ifndef CURL_DISABLE_IPFS + if(!uc && + (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; + } +#endif /* !CURL_DISABLE_IPFS */ + if(uc == CURLUE_OUT_OF_MEMORY) result = CURLE_OUT_OF_MEMORY; + curl_free(schemep); } else if(uc == CURLUE_OUT_OF_MEMORY) result = CURLE_OUT_OF_MEMORY; @@ -951,6 +962,13 @@ CURLcode config2setopts(struct OperationConfig *config, result = ssl_setopts(config, curl); if(setopt_bad(result)) return result; +#ifdef DEBUGBUILD + if(!per->urlnum) { + char *env = getenv("CURL_DBG_NO_USE_SSL_ON_FIRST"); + if(env) + my_setopt_enum(curl, CURLOPT_USE_SSL, CURLUSESSL_NONE); + } +#endif } if(config->path_as_is) diff --git a/src/curlinfo.c b/src/curlinfo.c index 13b9d62ce068..94a02e965f21 100644 --- a/src/curlinfo.c +++ b/src/curlinfo.c @@ -195,14 +195,14 @@ static const char *disabled[] = { #endif , "large-time: " -#if (SIZEOF_TIME_T < 5) +#if SIZEOF_TIME_T < 5 "OFF" #else "ON" #endif , "large-size: " -#if (SIZEOF_SIZE_T < 5) +#if SIZEOF_SIZE_T < 5 "OFF" #else "ON" diff --git a/src/mkhelp.pl b/src/mkhelp.pl index 053f74177619..9c60d7a64b6c 100755 --- a/src/mkhelp.pl +++ b/src/mkhelp.pl @@ -86,9 +86,9 @@ HEAD ; - my $c=0; + my $c = 0; for(split(//, $gzippedContent)) { - my $num=ord($_); + my $num = ord($_); if(!($c % 12)) { print " "; } @@ -105,7 +105,7 @@ { (void)opaque; /* not a typo, keep it curlx_calloc() */ - return (voidpf)curlx_calloc(items, size); + return curlx_calloc(items, size); } static void zfree_func(voidpf opaque, voidpf ptr) { diff --git a/src/tool_cb_dbg.c b/src/tool_cb_dbg.c index c9c14e6d1311..cd046dcbad08 100644 --- a/src/tool_cb_dbg.c +++ b/src/tool_cb_dbg.c @@ -134,7 +134,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, struct timeval tv; char timebuf[20]; /* largest signed 64-bit is: 9,223,372,036,854,775,807 - * max length in decimal: 1 + (6*3) = 19 + * max length in decimal: 1 + (6 * 3) = 19 * formatted via TRC_IDS_FORMAT_IDS_2 this becomes 2 + 19 + 1 + 19 + 2 = 43 * negative xfer-id are not printed, negative conn-ids use TRC_IDS_FORMAT_1 */ diff --git a/src/tool_cb_rea.c b/src/tool_cb_rea.c index a818d0804d64..b5bc53a46ed6 100644 --- a/src/tool_cb_rea.c +++ b/src/tool_cb_rea.c @@ -109,7 +109,7 @@ size_t tool_read_cb(char *buffer, size_t sz, size_t nmemb, void *userdata) #ifndef CURL_WINDOWS_UWP rc = sread(per->infd, buffer, curlx_uztosi(sz * nmemb)); if(rc < 0) { - if(SOCKERRNO == SOCKEWOULDBLOCK) { + if(SOCK_EAGAIN(SOCKERRNO)) { errno = 0; config->readbusy = TRUE; return CURL_READFUNC_PAUSE; diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c index d412a1ea88d7..133573a8b38e 100644 --- a/src/tool_cb_wrt.c +++ b/src/tool_cb_wrt.c @@ -125,12 +125,12 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, /* attempt to complete an incomplete UTF-8 sequence from previous call. the sequence does not have to be well-formed. */ if(outs->utf8seq[0] && rlen) { - bool complete = false; + bool complete = FALSE; /* two byte sequence (lead byte 110yyyyy) */ if(0xC0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xE0) { outs->utf8seq[1] = *rbuf++; --rlen; - complete = true; + complete = TRUE; } /* three byte sequence (lead byte 1110zzzz) */ else if(0xE0 <= outs->utf8seq[0] && outs->utf8seq[0] < 0xF0) { @@ -141,7 +141,7 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, if(rlen && !outs->utf8seq[2]) { outs->utf8seq[2] = *rbuf++; --rlen; - complete = true; + complete = TRUE; } } /* four byte sequence (lead byte 11110uuu) */ @@ -157,7 +157,7 @@ static size_t win_console(intptr_t fhnd, struct OutStruct *outs, if(rlen && !outs->utf8seq[3]) { outs->utf8seq[3] = *rbuf++; --rlen; - complete = true; + complete = TRUE; } } @@ -213,8 +213,7 @@ 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 *)curlx_realloc(global->term.buf, - len * sizeof(wchar_t)); + wchar_t *buf = curlx_realloc(global->term.buf, len * sizeof(wchar_t)); if(!buf) return CURL_WRITEFUNC_ERROR; global->term.len = len; diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 515df19073c9..3feb3e782471 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -219,7 +219,7 @@ void config_free(struct OperationConfig *config) * 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. + * the regular memory functions return so that mixup does 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 @@ -316,7 +316,7 @@ CURLcode globalconf_init(void) _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE; #endif - /* Initialise the global config */ + /* Initialize the global config */ global->showerror = FALSE; /* show errors when silent */ global->styled_output = TRUE; /* enable detection */ global->parallel_max = PARALLEL_DEFAULT; diff --git a/src/tool_doswin.c b/src/tool_doswin.c index 4b2a2a34b333..f836af95063c 100644 --- a/src/tool_doswin.c +++ b/src/tool_doswin.c @@ -549,7 +549,7 @@ SANITIZEcode sanitize_file_name(char ** const sanitized, const char *file_name, * 4. Windows Directory (e.g. C:\Windows) * 5. all directories along %PATH% * - * For WinXP and later search order actually depends on registry value: + * For Windows XP and later search order actually depends on registry value: * HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode */ CURLcode FindWin32CACert(struct OperationConfig *config, @@ -639,7 +639,7 @@ static struct TerminalSettings { LONG valid; } TerminalSettings; -/* Offered by mingw-w64 v7+. MS SDK ~10.16299/~VS2017+. */ +/* Offered by mingw-w64 v7+, MS SDK 10.0.10586.0/VS2015 Update 1+ */ #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif @@ -678,7 +678,7 @@ static void init_terminal(void) return; if((TerminalSettings.dwOutputMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) - tool_term_has_bold = true; + tool_term_has_bold = TRUE; else { /* The signal handler is set before attempting to change the console mode because otherwise a signal would not be caught after the change but @@ -688,7 +688,7 @@ static void init_terminal(void) if(SetConsoleMode(TerminalSettings.hStdOut, (TerminalSettings.dwOutputMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING))) { - tool_term_has_bold = true; + tool_term_has_bold = TRUE; atexit(restore_terminal); } else { @@ -770,10 +770,10 @@ curl_socket_t win32_stdin_read_thread(void) static curl_socket_t socket_r = CURL_SOCKET_BAD; if(socket_r != CURL_SOCKET_BAD) { - assert(stdin_thread != NULL); + assert(stdin_thread); return socket_r; } - assert(stdin_thread == NULL); + assert(!stdin_thread); do { curl_socklen_t socksize = 0; @@ -804,7 +804,7 @@ curl_socket_t win32_stdin_read_thread(void) break; } - /* Bind to any available loopback port */ + /* Retrieve the assigned loopback port/address */ if(getsockname(tdata->socket_l, (struct sockaddr *)&selfaddr, &socksize)) { errorf("getsockname error: %d", SOCKERRNO); break; diff --git a/src/tool_formparse.c b/src/tool_formparse.c index e129452d4362..7e6d625cb251 100644 --- a/src/tool_formparse.c +++ b/src/tool_formparse.c @@ -33,7 +33,7 @@ static struct tool_mime *tool_mime_new(struct tool_mime *parent, toolmimekind kind) { - struct tool_mime *m = (struct tool_mime *)curlx_calloc(1, sizeof(*m)); + struct tool_mime *m = curlx_calloc(1, sizeof(*m)); if(m) { m->kind = kind; @@ -192,8 +192,8 @@ void tool_mime_free(struct tool_mime *mime) } /* Mime part callbacks for stdin. */ -size_t tool_mime_stdin_read(char *buffer, - size_t size, size_t nitems, void *arg) +static size_t tool_mime_stdin_read(char *buffer, + size_t size, size_t nitems, void *arg) { struct tool_mime *sip = (struct tool_mime *)arg; curl_off_t bytesleft; @@ -217,7 +217,8 @@ size_t tool_mime_stdin_read(char *buffer, if(ferror(stdin)) { char errbuf[STRERROR_LEN]; /* Show error only once. */ - warnf("stdin: %s", curlx_strerror(errno, errbuf, sizeof(errbuf))); + warnf("Failed to read from stdin: %s", + curlx_strerror(errno, errbuf, sizeof(errbuf))); return CURL_READFUNC_ABORT; } } @@ -226,7 +227,7 @@ size_t tool_mime_stdin_read(char *buffer, return nitems; } -int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) +static int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) { struct tool_mime *sip = (struct tool_mime *)instream; @@ -250,71 +251,103 @@ int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence) /* Translate an internal mime tree into a libcurl mime tree. */ +#define MAX_FORMPARTS 100000 /* arbitrarily picked */ + static CURLcode tool2curlparts(CURL *curl, struct tool_mime *m, curl_mime *mime) { CURLcode result = CURLE_OK; - curl_mimepart *part = NULL; - curl_mime *submime = NULL; - const char *filename = NULL; + struct tool_mime *curr; + struct tool_mime **nodes = NULL; + int count; + int i; + + if(!m) + return CURLE_OK; + + for(curr = m, count = 0; curr; curr = curr->prev) { + if(count > MAX_FORMPARTS) + return CURLE_BAD_FUNCTION_ARGUMENT; + count++; + } - if(m) { - result = tool2curlparts(curl, m->prev, mime); - if(!result) { - part = curl_mime_addpart(mime); - if(!part) - result = CURLE_OUT_OF_MEMORY; - } - if(!result) { - filename = m->filename; - switch(m->kind) { - case TOOLMIME_PARTS: - result = tool2curlmime(curl, m, &submime); - if(!result) { - result = curl_mime_subparts(part, submime); - if(result) - curl_mime_free(submime); - } - break; + nodes = curlx_malloc(sizeof(struct tool_mime *) * count); + if(!nodes) + return CURLE_OUT_OF_MEMORY; - case TOOLMIME_DATA: - result = curl_mime_data(part, m->data, CURL_ZERO_TERMINATED); - break; + /* populate array from the end to the beginning */ + curr = m; + for(i = count - 1; i >= 0; i--) { + nodes[i] = curr; + curr = curr->prev; + } - case TOOLMIME_FILE: - case TOOLMIME_FILEDATA: - result = curl_mime_filedata(part, m->data); - if(!result && m->kind == TOOLMIME_FILEDATA && !filename) - result = curl_mime_filename(part, NULL); - break; + for(i = 0; i < count; i++) { + struct tool_mime *node = nodes[i]; + curl_mimepart *part = NULL; + curl_mime *submime = NULL; + const char *filename = node->filename; - case TOOLMIME_STDIN: - if(!filename) - filename = "-"; - FALLTHROUGH(); - case TOOLMIME_STDINDATA: - 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; + part = curl_mime_addpart(mime); + if(!part) { + result = CURLE_OUT_OF_MEMORY; + break; + } - default: - /* Other cases not possible in this context. */ - break; + switch(node->kind) { + case TOOLMIME_PARTS: + result = tool2curlmime(curl, node, &submime); + if(!result) { + result = curl_mime_subparts(part, submime); + if(result) + curl_mime_free(submime); } + break; + + case TOOLMIME_DATA: + result = curl_mime_data(part, node->data, CURL_ZERO_TERMINATED); + break; + + case TOOLMIME_FILE: + case TOOLMIME_FILEDATA: + result = curl_mime_filedata(part, node->data); + if(!result && node->kind == TOOLMIME_FILEDATA && !filename) + result = curl_mime_filename(part, NULL); + break; + + case TOOLMIME_STDIN: + if(!filename) + filename = "-"; + FALLTHROUGH(); + case TOOLMIME_STDINDATA: + result = curl_mime_data_cb(part, node->size, + tool_mime_stdin_read, + tool_mime_stdin_seek, + NULL, node); + break; + + default: + /* Other cases not possible in this context. */ + break; } + + /* Common part configuration */ if(!result && filename) result = curl_mime_filename(part, filename); if(!result) - result = curl_mime_type(part, m->type); + result = curl_mime_type(part, node->type); if(!result) - result = curl_mime_headers(part, m->headers, 0); + result = curl_mime_headers(part, node->headers, 0); if(!result) - result = curl_mime_encoder(part, m->encoder); + result = curl_mime_encoder(part, node->encoder); if(!result) - result = curl_mime_name(part, m->name); + result = curl_mime_name(part, node->name); + + if(result) + break; } + + curlx_free(nodes); return result; } @@ -408,14 +441,17 @@ static int slist_append(struct curl_slist **plist, const char *data) return 0; } -/* Read headers from a file and append to list. */ +#define HEADER_LINE_BUFFER_SIZE 8192 + +/* Read headers from a file and append to list. + Return zero on success, non-zero on error. */ static int read_field_headers(FILE *fp, struct curl_slist **pheaders) { struct dynbuf line; bool error = FALSE; int err = 0; - curlx_dyn_init(&line, 8092); + curlx_dyn_init(&line, HEADER_LINE_BUFFER_SIZE); while(my_get_line(fp, &line, &error)) { const char *ptr = curlx_dyn_ptr(&line); size_t len = curlx_dyn_len(&line); @@ -436,7 +472,7 @@ static int read_field_headers(FILE *fp, struct curl_slist **pheaders) /* append this new line onto the previous line */ struct dynbuf amend; struct curl_slist *l = *pheaders; - curlx_dyn_init(&amend, 8092); + curlx_dyn_init(&amend, HEADER_LINE_BUFFER_SIZE); /* find the last node */ while(l && l->next) l = l->next; @@ -450,14 +486,12 @@ static int read_field_headers(FILE *fp, struct curl_slist **pheaders) 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; - } + if(!l->data) + err = -1; } - else { + else err = slist_append(pheaders, ptr); - } + if(err) { errorf("Out of memory for field headers"); err = -1; @@ -472,6 +506,163 @@ static int read_field_headers(FILE *fp, struct curl_slist **pheaders) return err; } +static void param_type(char **ptr, char **ptype, char **endct, char *sep) +{ + char *p = *ptr; + size_t tlen; + for(p += sizeof("type=") - 1; ISBLANK(*p); p++) + ; + /* set type pointer */ + *ptype = p; + + /* find end of content-type */ + tlen = strcspn(p, "()<>@,;:\\\"[]?=\r\n "); + p += tlen; + *endct = p; + *sep = *p; + *ptr = p; +} + +static void param_filename(char **ptr, char **endct, char **pfilename, + char endchar, char *sep) +{ + char *p = *ptr; + char *endpos; + char *tp; + + if(*endct) { + **endct = '\0'; + *endct = NULL; + } + for(p += sizeof("filename=") - 1; ISBLANK(*p); p++) + ; + tp = p; + *pfilename = get_param_word(&p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(*pfilename == tp) + while(endpos > *pfilename && ISBLANK(endpos[-1])) + endpos--; + *sep = *p; + *endpos = '\0'; + *ptr = p; +} + +static int param_headers(char **ptr, char **endct, + struct curl_slist **pheaders, char endchar, char *sep) +{ + char *p = *ptr; + char *endpos; + char *tp; + + if(*endct) { + **endct = '\0'; + *endct = NULL; + } + p += sizeof("headers=") - 1; + if(*p == '@' || *p == '<') { + char *hdrfile; + FILE *fp; + /* Read headers from a file. */ + do { + p++; + } while(ISBLANK(*p)); + tp = p; + hdrfile = get_param_word(&p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(hdrfile == tp) + while(endpos > hdrfile && ISBLANK(endpos[-1])) + endpos--; + *sep = *p; + *endpos = '\0'; + fp = curlx_fopen(hdrfile, FOPEN_READTEXT); + if(!fp) { + char errbuf[STRERROR_LEN]; + warnf("Cannot read from %s: %s", hdrfile, + curlx_strerror(errno, errbuf, sizeof(errbuf))); + } + else { + int i = read_field_headers(fp, pheaders); + + curlx_fclose(fp); + if(i) { + curl_slist_free_all(*pheaders); + return -1; + } + } + } + else { + char *hdr; + + while(ISBLANK(*p)) + p++; + tp = p; + hdr = get_param_word(&p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(hdr == tp) + while(endpos > hdr && ISBLANK(endpos[-1])) + endpos--; + *sep = *p; + *endpos = '\0'; + if(slist_append(pheaders, hdr)) { + errorf("Out of memory for field header"); + curl_slist_free_all(*pheaders); + return -1; + } + } + *ptr = p; + return 0; +} + +static void param_encoder(char **ptr, char **endct, char **pencoder, + char endchar, char *sep) +{ + char *p = *ptr; + char *endpos; + char *tp; + + if(*endct) { + **endct = '\0'; + *endct = NULL; + } + for(p += sizeof("encoder=") - 1; ISBLANK(*p); p++) + ; + tp = p; + *pencoder = get_param_word(&p, &endpos, endchar); + /* If not quoted, strip trailing spaces. */ + if(*pencoder == tp) + while(endpos > *pencoder && ISBLANK(endpos[-1])) + endpos--; + *sep = *p; + *endpos = '\0'; + *ptr = p; +} + +/** + * Parses a single parameter part and its associated metadata from a string. + * + * This function extracts a primary data word and scans for optional + * semicolon-separated attributes including 'type=', 'filename=', 'headers=', + * and 'encoder='. + * + * Used for parsing command-line form arguments or multipart/form-data + * attributes. + * + * @param endchar The character that signifies the end of the entire + * parameter block (e.g., ',' or '\0'). + * @param str Pointer to the current position in the input string. + * Updated to point at the delimiter or terminator that + * ended the parsed part. + * @param pdata Pointer to a char * that receives the primary data word. + * @param ptype [out] Optional. Receives the extracted 'type=' value. + * @param pfilename [out] Optional. Receives the extracted 'filename=' value. + * @param pencoder [out] Optional. Receives the extracted 'encoder=' value. + * @param pheaders [out] Optional. Receives a pointer to a curl_slist + * containing extracted 'headers='. + * + * @return The character that terminated the parsing (casted to int), + * or -1 on memory or parsing error. + */ + static int get_param_part(char endchar, char **str, char **pdata, char **ptype, char **pfilename, char **pencoder, @@ -509,108 +700,16 @@ static int get_param_part(char endchar, while(p++ && ISBLANK(*p)) ; - if(!endct && checkprefix("type=", p)) { - size_t tlen; - for(p += 5; ISBLANK(*p); p++) - ; - /* set type pointer */ - type = p; - - /* find end of content-type */ - tlen = strcspn(p, "()<>@,;:\\\"[]?=\r\n "); - p += tlen; - endct = p; - sep = *p; - } - else if(checkprefix("filename=", p)) { - if(endct) { - *endct = '\0'; - endct = NULL; - } - for(p += 9; ISBLANK(*p); p++) - ; - tp = p; - filename = get_param_word(&p, &endpos, endchar); - /* If not quoted, strip trailing spaces. */ - if(filename == tp) - while(endpos > filename && ISBLANK(endpos[-1])) - endpos--; - sep = *p; - *endpos = '\0'; - } + if(!endct && checkprefix("type=", p)) + param_type(&p, &type, &endct, &sep); + else if(checkprefix("filename=", p)) + param_filename(&p, &endct, &filename, endchar, &sep); else if(checkprefix("headers=", p)) { - if(endct) { - *endct = '\0'; - endct = NULL; - } - p += 8; - if(*p == '@' || *p == '<') { - char *hdrfile; - FILE *fp; - /* Read headers from a file. */ - do { - p++; - } while(ISBLANK(*p)); - tp = p; - hdrfile = get_param_word(&p, &endpos, endchar); - /* If not quoted, strip trailing spaces. */ - if(hdrfile == tp) - while(endpos > hdrfile && ISBLANK(endpos[-1])) - endpos--; - sep = *p; - *endpos = '\0'; - fp = curlx_fopen(hdrfile, FOPEN_READTEXT); - if(!fp) { - char errbuf[STRERROR_LEN]; - warnf("Cannot read from %s: %s", hdrfile, - curlx_strerror(errno, errbuf, sizeof(errbuf))); - } - else { - int i = read_field_headers(fp, &headers); - - curlx_fclose(fp); - if(i) { - curl_slist_free_all(headers); - return -1; - } - } - } - else { - char *hdr; - - while(ISBLANK(*p)) - p++; - tp = p; - hdr = get_param_word(&p, &endpos, endchar); - /* If not quoted, strip trailing spaces. */ - if(hdr == tp) - while(endpos > hdr && ISBLANK(endpos[-1])) - endpos--; - sep = *p; - *endpos = '\0'; - if(slist_append(&headers, hdr)) { - errorf("Out of memory for field header"); - curl_slist_free_all(headers); - return -1; - } - } - } - else if(checkprefix("encoder=", p)) { - if(endct) { - *endct = '\0'; - endct = NULL; - } - for(p += 8; ISBLANK(*p); p++) - ; - tp = p; - encoder = get_param_word(&p, &endpos, endchar); - /* If not quoted, strip trailing spaces. */ - if(encoder == tp) - while(endpos > encoder && ISSPACE(endpos[-1])) - endpos--; - sep = *p; - *endpos = '\0'; + if(param_headers(&p, &endct, &headers, endchar, &sep)) + return -1; } + else if(checkprefix("encoder=", p)) + param_encoder(&p, &endct, &encoder, endchar, &sep); else if(endct) { /* This is part of content type. */ for(endct = p; *p && *p != ';' && *p != endchar; p++) @@ -692,7 +791,7 @@ static int get_param_part(char endchar, * file and do like this: * * 'name=foo;headers=@headerfile' or why not - * 'name=@filemame;headers=@headerfile' + * 'name=@filename;headers=@headerfile' * * To upload a file, but to fake the filename that is included in the * formpost, do like this: @@ -701,8 +800,8 @@ static int get_param_part(char endchar, * 'name=@filename;filename="play, play, and play.txt"' * * If filename/path contains ',' or ';', it must be quoted by double-quotes, - * else curl fails to figure out the correct filename. if the filename - * tobe quoted contains '"' or '\', '"' and '\' must be escaped by backslash. + * else curl fails to figure out the correct filename. if the filename to be + * quoted contains '"' or '\', '"' and '\' must be escaped by backslash. * ***************************************************************************/ @@ -826,7 +925,7 @@ int formparse(const char *input, SET_TOOL_MIME_PTR(part, encoder); /* *contp could be '\0', so we check with the delimiter */ - } while(sep); /* loop if there is another filename */ + } while(sep == ','); /* loop if there is another filename */ part = (*mimecurrent)->subparts; /* Set name on group. */ } else { diff --git a/src/tool_formparse.h b/src/tool_formparse.h index d2222330eef9..9df5cb7b7a8f 100644 --- a/src/tool_formparse.h +++ b/src/tool_formparse.h @@ -57,10 +57,6 @@ struct tool_mime { curl_off_t curpos; /* Stdin current read position. */ }; -size_t tool_mime_stdin_read(char *buffer, - size_t size, size_t nitems, void *arg); -int tool_mime_stdin_seek(void *instream, curl_off_t offset, int whence); - int formparse(const char *input, struct tool_mime **mimeroot, struct tool_mime **mimecurrent, diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 176d3ebc3849..35c01b1d92d2 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -43,10 +43,8 @@ static ParameterError getstr(char **str, const char *val, bool allowblank) { - if(*str) { - curlx_free(*str); - *str = NULL; - } + if(*str) + curlx_safefree(*str); DEBUGASSERT(val); if(!allowblank && !val[0]) return PARAM_BLANK_STRING; @@ -61,10 +59,8 @@ static ParameterError getstr(char **str, const char *val, bool allowblank) static ParameterError getstrn(char **str, const char *val, size_t len, bool allowblank) { - if(*str) { - curlx_free(*str); - *str = NULL; - } + if(*str) + curlx_safefree(*str); DEBUGASSERT(val); if(!allowblank && !val[0]) return PARAM_BLANK_STRING; @@ -254,6 +250,7 @@ static const struct LongShort aliases[]= { {"proxy-digest", ARG_BOOL, ' ', C_PROXY_DIGEST}, {"proxy-header", ARG_STRG, ' ', C_PROXY_HEADER}, {"proxy-http2", ARG_BOOL, ' ', C_PROXY_HTTP2}, + {"proxy-http3", ARG_BOOL, ' ', C_PROXY_HTTP3}, {"proxy-insecure", ARG_BOOL, ' ', C_PROXY_INSECURE}, {"proxy-key", ARG_FILE|ARG_TLS, ' ', C_PROXY_KEY}, {"proxy-key-type", ARG_STRG|ARG_TLS, ' ', C_PROXY_KEY_TYPE}, @@ -542,11 +539,11 @@ struct sizeunit { 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 */ + { '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; @@ -1452,7 +1449,7 @@ static ParameterError parse_range(struct OperationConfig *config, curlx_str_single(&nextarg, '-')) { /* 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 + 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 (-). " @@ -1630,12 +1627,12 @@ static ParameterError parse_time_cond(struct OperationConfig *config, config->timecond = CURL_TIMECOND_IFMODSINCE; break; case '-': - /* If-Unmodified-Since: (section 14.24 in RFC2068) */ + /* If-Unmodified-Since: (section 14.24 in RFC2068) */ config->timecond = CURL_TIMECOND_IFUNMODSINCE; nextarg++; break; case '=': - /* Last-Modified: (section 14.29 in RFC2068) */ + /* Last-Modified: (section 14.29 in RFC2068) */ config->timecond = CURL_TIMECOND_LASTMOD; nextarg++; break; @@ -1911,6 +1908,10 @@ static ParameterError opt_bool(struct OperationConfig *config, if(config->ftp_ssl) warnf("--%s is an insecure option, consider --ssl-reqd instead", a->lname); + if(toggle && config->ftp_ssl_control) { + config->ftp_ssl_control = FALSE; + warnf("--%s overrides --ftp-ssl-control", a->lname); + } break; case C_FTP_SSL_CCC: /* --ftp-ssl-ccc */ config->ftp_ssl_ccc = toggle; @@ -1962,6 +1963,10 @@ static ParameterError opt_bool(struct OperationConfig *config, break; case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */ config->ftp_ssl_control = toggle; + if(toggle && config->ftp_ssl) { + config->ftp_ssl = FALSE; + warnf("--%s overrides --ssl", a->lname); + } break; case C_RAW: /* --raw */ config->raw = toggle; @@ -2028,6 +2033,18 @@ static ParameterError opt_bool(struct OperationConfig *config, config->proxyver = toggle ? CURLPROXY_HTTPS2 : CURLPROXY_HTTPS; break; + case C_PROXY_HTTP3: /* --proxy-http3 */ +#ifndef USE_PROXY_HTTP3 + if(toggle) + return PARAM_LIBCURL_DOESNT_SUPPORT; + config->proxyver = CURLPROXY_HTTPS; +#else + if(!feature_httpsproxy || !feature_http3) + return PARAM_LIBCURL_DOESNT_SUPPORT; + + config->proxyver = toggle ? CURLPROXY_HTTPS3 : CURLPROXY_HTTPS; +#endif + break; case C_APPEND: /* --append */ config->ftp_append = toggle; break; @@ -2768,10 +2785,7 @@ static ParameterError opt_string(struct OperationConfig *config, } break; case C_HOSTPUBSHA256: /* --hostpubsha256 */ - if(!feature_libssh2) - err = PARAM_LIBCURL_DOESNT_SUPPORT; - else - err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); + err = getstr(&config->hostpubsha256, nextarg, DENY_BLANK); break; case C_TLSUSER: /* --tlsuser */ if(!feature_tls_srp) @@ -2902,7 +2916,8 @@ static ParameterError opt_string(struct OperationConfig *config, case C_PROXY: /* --proxy */ /* --proxy */ err = getstr(&config->proxy, nextarg, ALLOW_BLANK); - if(config->proxyver != CURLPROXY_HTTPS2) + if(config->proxyver != CURLPROXY_HTTPS2 && + config->proxyver != CURLPROXY_HTTPS3) config->proxyver = CURLPROXY_HTTP; break; case C_REQUEST: /* --request */ diff --git a/src/tool_getparam.h b/src/tool_getparam.h index e137cc322f98..32476d377691 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -201,6 +201,7 @@ typedef enum { C_PROXY_DIGEST, C_PROXY_HEADER, C_PROXY_HTTP2, + C_PROXY_HTTP3, C_PROXY_INSECURE, C_PROXY_KEY, C_PROXY_KEY_TYPE, diff --git a/src/tool_getpass.c b/src/tool_getpass.c index 68a16cab3e61..f0eeebcc4164 100644 --- a/src/tool_getpass.c +++ b/src/tool_getpass.c @@ -58,7 +58,7 @@ char *getpass_r(const char *prompt, char *buffer, size_t buflen) long sts; short chan; - /* iosbdef.h was not in VAX V7.2 or CC 6.4 */ + /* 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 */ diff --git a/src/tool_help.c b/src/tool_help.c index 212972f38248..c9d76d5d2072 100644 --- a/src/tool_help.c +++ b/src/tool_help.c @@ -166,8 +166,9 @@ void inithelpscan(struct scan_ctx *ctx, ctx->flen = strlen(arg); ctx->endarg = endarg; ctx->elen = strlen(endarg); - DEBUGASSERT((ctx->elen < sizeof(ctx->rbuf)) || - (ctx->flen < sizeof(ctx->rbuf))); + DEBUGASSERT((ctx->elen < sizeof(ctx->rbuf)) && + (ctx->flen < sizeof(ctx->rbuf)) && + (ctx->tlen < sizeof(ctx->rbuf))); ctx->show = 0; ctx->olen = 0; memset(ctx->rbuf, 0, sizeof(ctx->rbuf)); diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c index 5a5382c00701..9aee23428090 100644 --- a/src/tool_libinfo.c +++ b/src/tool_libinfo.c @@ -71,7 +71,6 @@ bool feature_http2 = FALSE; bool feature_http3 = FALSE; bool feature_httpsproxy = FALSE; bool feature_libz = FALSE; -bool feature_libssh2 = FALSE; bool feature_ntlm = FALSE; bool feature_ntlm_wb = FALSE; bool feature_spnego = FALSE; @@ -183,9 +182,6 @@ CURLcode get_libcurl_info(void) ++feature_count; } - feature_libssh2 = curlinfo->age >= CURLVERSION_FOURTH && - curlinfo->libssh_version && - !strncmp("libssh2", curlinfo->libssh_version, 7); return CURLE_OK; } diff --git a/src/tool_libinfo.h b/src/tool_libinfo.h index ddc41a133867..e8c3517a9b2a 100644 --- a/src/tool_libinfo.h +++ b/src/tool_libinfo.h @@ -54,7 +54,6 @@ extern bool feature_http2; extern bool feature_http3; extern bool feature_httpsproxy; extern bool feature_libz; -extern bool feature_libssh2; extern bool feature_ntlm; extern bool feature_ntlm_wb; extern bool feature_spnego; diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index 864771bfba88..c0b0af792f9e 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -542,6 +542,9 @@ const struct helptxt helptext[] = { { " --proxy-http2", "Use HTTP/2 with HTTPS proxy", CURLHELP_HTTP | CURLHELP_PROXY }, + { " --proxy-http3", + "Use HTTP/3 with HTTPS proxy", + CURLHELP_HTTP | CURLHELP_PROXY }, { " --proxy-insecure", "Skip HTTPS proxy cert verification", CURLHELP_PROXY | CURLHELP_TLS }, diff --git a/src/tool_main.c b/src/tool_main.c index e5e4659b1585..5ee8fc9ddd96 100644 --- a/src/tool_main.c +++ b/src/tool_main.c @@ -161,7 +161,7 @@ int main(int argc, char *argv[]) /* win32_init must be called before other init routines. */ result = win32_init(); if(result) { - errorf("(%d) Windows-specific init failed", result); + errorf("(%d) Windows-specific init failed", (int)result); return (int)result; } #endif diff --git a/src/tool_operate.c b/src/tool_operate.c index 3ad30ca4c4e8..c9bb63682701 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -616,7 +616,7 @@ static CURLcode post_check_result(struct per_transfer *per, CURLcode result) if(!config->synthetic_error && result && (!global->silent || global->showerror)) { const char *msg = per->errorbuffer; - curl_mfprintf(tool_stderr, "curl: (%d) %s\n", result, + curl_mfprintf(tool_stderr, "curl: (%d) %s\n", (int)result, msg[0] ? msg : curl_easy_strerror(result)); if(result == CURLE_PEER_FAILED_VERIFICATION) fputs(CURL_CA_CERT_ERRORMSG, tool_stderr); @@ -697,7 +697,7 @@ static CURLcode post_close_output(struct per_transfer *per, if(!result && rc) { /* something went wrong in the writing process */ result = CURLE_WRITE_ERROR; - errorf("curl: (%d) Failed writing body", result); + errorf("curl: (%d) Failed writing body", (int)result); } if(result && config->rm_partial) { curlx_struct_stat st; @@ -722,6 +722,7 @@ static CURLcode post_close_output(struct per_transfer *per, } return result; } + /* * Call this after a transfer has completed. */ @@ -855,7 +856,7 @@ static CURLcode append2query(struct OperationConfig *config, if(uerr) { result = urlerr_cvt(uerr); errorf("(%d) Could not parse the URL, " - "failed to set query", result); + "failed to set query", (int)result); config->synthetic_error = TRUE; } else { @@ -1052,11 +1053,13 @@ static CURLcode setup_outfile(struct OperationConfig *config, return result; } } - else if(glob_inuse(&state->urlglob)) { - /* fill '#1' ... '#9' terms from URL pattern */ + else if(glob_inuse(&state->urlglob) || glob_inuse(&state->inglob)) { + /* expand '#1' ... '#9' references from URL pattern and named references + from the upload file glob */ SANITIZEcode sc; CURLcode result = - glob_match_url(&per->outfile, u->outfile, &state->urlglob, &sc); + glob_match_url(&per->outfile, u->outfile, &state->urlglob, + glob_inuse(&state->inglob) ? &state->inglob : NULL, &sc); if(sc) { if(sc == SANITIZE_ERR_OUT_OF_MEMORY) @@ -1067,7 +1070,12 @@ static CURLcode setup_outfile(struct OperationConfig *config, } else if(result) { /* bad globbing */ - warnf("bad output glob"); + if(state->urlglob.error) { + glob_show_error(&state->urlglob, u->outfile, tool_stderr, result); + config->synthetic_error = TRUE; + } + else + warnf("bad output glob"); return result; } if(!*per->outfile) { @@ -1701,9 +1709,7 @@ static int cb_timeout(CURLM *multi, long timeout_ms, void *userp) static struct contextuv *create_context(curl_socket_t sockfd, struct datauv *uv) { - struct contextuv *c; - - c = (struct contextuv *)curlx_malloc(sizeof(*c)); + struct contextuv *c = curlx_malloc(sizeof(*c)); c->sockfd = sockfd; c->uv = uv; @@ -2452,7 +2458,7 @@ CURLcode operate(int argc, argv_item_t argv[]) } else { if(global->libcurl) { - /* Initialise the libcurl source output */ + /* Initialize the libcurl source output */ result = easysrc_init(); } diff --git a/src/tool_operhlp.c b/src/tool_operhlp.c index 0e360cbcbde5..316393abb8af 100644 --- a/src/tool_operhlp.c +++ b/src/tool_operhlp.c @@ -175,6 +175,8 @@ CURLcode add_file_name_to_url(CURL *curl, char **inurlp, const char *filename) return result; } +#define DEFAULT_FILENAME "curl_response" + /* Extracts the name portion of the URL. * Returns a pointer to a heap-allocated string or NULL if * no name part, at location indicated by first argument. @@ -218,8 +220,9 @@ CURLcode get_url_file_name(char **filename, const char *url, SANITIZEcode *sc) } else { /* no slash => empty string, use default */ - *filename = curlx_strdup("curl_response"); - warnf("No remote filename, uses \"%s\"", *filename); + *filename = curlx_strdup(DEFAULT_FILENAME); + if(*filename) + warnf("No remote filename, uses \"" DEFAULT_FILENAME "\""); } curl_free(path); diff --git a/src/tool_parsecfg.c b/src/tool_parsecfg.c index f5910f404e77..e7d3a3f87827 100644 --- a/src/tool_parsecfg.c +++ b/src/tool_parsecfg.c @@ -77,15 +77,18 @@ static int unslashquote(const char *line, struct dynbuf *param) return 0; /* ok */ } -/* return 0 on everything-is-fine, and non-zero otherwise */ -ParameterError parseconfig(const char *filename, int max_recursive, - char **resolved) +/* + * Open the config file. When filename is NULL, tries to find .curlrc in the + * home directory (and on Windows, in the executable directory). Updates + * *namep to the effective filename and *pathalloc to any allocated path + * that must be freed by the caller. Returns the opened FILE or NULL. + */ +static FILE *open_config_file(const char *filename, + const char **namep, + char **pathalloc) { FILE *file = NULL; - bool usedarg = FALSE; - ParameterError err = PARAM_OK; - struct OperationConfig *config = global->last; - char *pathalloc = NULL; + *pathalloc = NULL; if(!filename) { /* NULL means load .curlrc from homedir! */ @@ -94,9 +97,9 @@ ParameterError parseconfig(const char *filename, int max_recursive, file = curlx_fopen(curlrc, FOPEN_READTEXT); if(!file) { curlx_free(curlrc); - return PARAM_READ_ERROR; + return NULL; } - filename = pathalloc = curlrc; + *namep = *pathalloc = curlrc; } #ifdef _WIN32 else { @@ -107,16 +110,154 @@ ParameterError parseconfig(const char *filename, int max_recursive, file = tool_execpath("_curlrc", &fullp); if(file) /* this is the filename we read from */ - filename = fullp; + *namep = fullp; } #endif } else { if(strcmp(filename, "-")) file = curlx_fopen(filename, FOPEN_READTEXT); - else + else { file = stdin; + *namep = ""; + } } + return file; +} + +/* + * Extract the parameter value from a config line. The line pointer should + * be positioned after the option keyword has been null-terminated. + * Skips separators and whitespace, then handles quoted and unquoted + * parameter values. Sets *param_out to the parameter string; unquoted empty + * values set it to NULL, while quoted empty values become an empty string. + */ +static ParameterError extract_param(char *line, + bool dashed_option, + struct dynbuf *pbuf, + const char *filename, + int lineno, + const char *option, + char **param_out) +{ + /* pass spaces and separator(s) */ + while(ISBLANK(*line) || ISSEP(*line, dashed_option)) + line++; + + /* the parameter starts here (unless quoted) */ + if(*line == '\"') { + /* quoted parameter, do the quote dance */ + int rc = unslashquote(++line, pbuf); + if(rc) + return PARAM_BAD_USE; + *param_out = curlx_dyn_len(pbuf) ? curlx_dyn_ptr(pbuf) : CURL_UNCONST(""); + } + else { + if(*line == '\'') { + warnf("%s:%d Option '%s' uses argument with leading single quote. " + "It is probably a mistake. Consider double quotes.", + filename, lineno, option); + } + *param_out = line; /* parameter starts here */ + while(*line && !ISSPACE(*line)) /* stop also on CRLF */ + line++; + + if(*line) { + *line = '\0'; /* null-terminate */ + + /* to detect mistakes better, see if there is data following */ + line++; + /* pass all spaces */ + while(ISBLANK(*line)) + line++; + + switch(*line) { + case '\0': + case '\r': + case '\n': + case '#': /* comment */ + break; + default: + warnf("%s:%d Option '%s' uses argument with unquoted whitespace. " + "This may cause side-effects. Consider double quotes.", + filename, lineno, option); + } + } + if(!**param_out) + /* do this so getparameter can check for required parameters. + Otherwise it always thinks there is a parameter. */ + *param_out = NULL; + } + return PARAM_OK; +} + +/* + * Process the result from getparameter. Handles PARAM_NEXT_OPERATION + * by allocating a new config, and reports errors for other non-OK results. + * Updates *configp if a new operation config is allocated. + * Returns PARAM_OK if processing should continue, or an error code. + */ +static ParameterError process_config_result(ParameterError res, + struct OperationConfig **configp, + const char *param, + bool usedarg, + const char *filename, + int lineno, + const char *option) +{ + if(!res && param && *param && !usedarg) + /* we passed in a parameter that was not used! */ + res = PARAM_GOT_EXTRA_PARAMETER; + + if(res == PARAM_NEXT_OPERATION) { + struct OperationConfig *config = *configp; + if(config->url_list && config->url_list->url) { + /* Allocate the next config */ + config->next = config_alloc(); + if(config->next) { + /* Update the last operation pointer */ + global->last = config->next; + + /* Move onto the new config */ + config->next->prev = config; + *configp = config->next; + } + else + res = PARAM_NO_MEM; + } + } + + if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) { + const char *display = filename; + /* the help request is not really an error */ + if(!strcmp(filename, "-")) + display = ""; + if(res != PARAM_HELP_REQUESTED && + res != PARAM_MANUAL_REQUESTED && + res != PARAM_VERSION_INFO_REQUESTED && + res != PARAM_ENGINES_REQUESTED && + res != PARAM_CA_EMBED_REQUESTED) { + const char *reason = param2text(res); + errorf("%s:%d config file option '%s' %s", + display, lineno, option, reason); + if(res == PARAM_OPTION_UNKNOWN) + res = PARAM_CONFIG_OPTION_UNKNOWN; + return res; + } + } + return PARAM_OK; +} + +ParameterError parseconfig(const char *filename, int max_recursive, + char **resolved) +{ + FILE *file = NULL; + bool usedarg = FALSE; + ParameterError err = PARAM_OK; + struct OperationConfig *config = global->last; + char *pathalloc = NULL; + + file = open_config_file(filename, &filename, &pathalloc); if(file) { char *line; @@ -151,109 +292,20 @@ ParameterError parseconfig(const char *filename, int max_recursive, /* ... and has ended here */ if(*line) - *line++ = '\0'; /* null-terminate, we have a local copy of the data */ - -#ifdef DEBUG_CONFIG - curl_mfprintf(tool_stderr, "GOT: %s\n", option); -#endif - - /* pass spaces and separator(s) */ - while(ISBLANK(*line) || ISSEP(*line, dashed_option)) - line++; - - /* the parameter starts here (unless quoted) */ - if(*line == '\"') { - /* quoted parameter, do the quote dance */ - int rc = unslashquote(++line, &pbuf); - if(rc) { - err = PARAM_BAD_USE; - break; - } - param = curlx_dyn_len(&pbuf) ? curlx_dyn_ptr(&pbuf) : CURL_UNCONST(""); - } - else { - if(*line == '\'') { - warnf("%s:%d Option '%s' uses argument with leading single quote. " - "It is probably a mistake. Consider double quotes.", - filename, lineno, option); - } - param = line; /* parameter starts here */ - while(*line && !ISSPACE(*line)) /* stop also on CRLF */ - line++; - - if(*line) { - *line = '\0'; /* null-terminate */ + *line++ = '\0'; /* null-terminate, we have a local copy */ - /* to detect mistakes better, see if there is data following */ - line++; - /* pass all spaces */ - while(ISBLANK(*line)) - line++; - - switch(*line) { - case '\0': - case '\r': - case '\n': - case '#': /* comment */ - break; - default: - warnf("%s:%d Option '%s' uses argument with unquoted whitespace. " - "This may cause side-effects. Consider double quotes.", - filename, lineno, option); - } - } - if(!*param) - /* do this so getparameter can check for required parameters. - Otherwise it always thinks there is a parameter. */ - param = NULL; - } + /* if there is a parameter for this option, extract it */ + err = extract_param(line, dashed_option, &pbuf, filename, lineno, + option, ¶m); + if(err) + break; -#ifdef DEBUG_CONFIG - curl_mfprintf(tool_stderr, "PARAM: \"%s\"\n", - (param ? param : "(null)")); -#endif res = getparameter(option, param, &usedarg, config, max_recursive); - config = global->last; - if(!res && param && *param && !usedarg) - /* we passed in a parameter that was not used! */ - res = PARAM_GOT_EXTRA_PARAMETER; - - if(res == PARAM_NEXT_OPERATION) { - if(config->url_list && config->url_list->url) { - /* Allocate the next config */ - config->next = config_alloc(); - if(config->next) { - /* Update the last operation pointer */ - global->last = config->next; - - /* Move onto the new config */ - config->next->prev = config; - config = config->next; - } - else - res = PARAM_NO_MEM; - } - } + config = global->last; - if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) { - /* the help request is not really an error */ - if(!strcmp(filename, "-")) { - filename = ""; - } - if(res != PARAM_HELP_REQUESTED && - res != PARAM_MANUAL_REQUESTED && - res != PARAM_VERSION_INFO_REQUESTED && - res != PARAM_ENGINES_REQUESTED && - res != PARAM_CA_EMBED_REQUESTED) { - const char *reason = param2text(res); - errorf("%s:%d config file option '%s' %s", - filename, lineno, option, reason); - if(res == PARAM_OPTION_UNKNOWN) - res = PARAM_CONFIG_OPTION_UNKNOWN; - err = res; - } - } + err = process_config_result(res, &config, param, usedarg, filename, + lineno, option); } curlx_dyn_free(&buf); curlx_dyn_free(&pbuf); diff --git a/src/tool_setopt.c b/src/tool_setopt.c index 6dbf8baa4cdb..2174571ad4d8 100644 --- a/src/tool_setopt.c +++ b/src/tool_setopt.c @@ -226,8 +226,10 @@ static char *c_escape(const char *str, curl_off_t len) result = curlx_dyn_addn(&escaped, str, s - str); if(!result) - (void)!curlx_dyn_addn(&escaped, "...", cutoff); + result = curlx_dyn_addn(&escaped, "...", cutoff); + if(result) + return NULL; return curlx_dyn_ptr(&escaped); } diff --git a/src/tool_setup.h b/src/tool_setup.h index 7a5f597bbbd6..7e77c114f1c4 100644 --- a/src/tool_setup.h +++ b/src/tool_setup.h @@ -102,6 +102,8 @@ int toolx_ftruncate_win32(int fd, curl_off_t where); #elif defined(__DJGPP__) int toolx_ftruncate_djgpp(int fd, curl_off_t where); #define toolx_ftruncate toolx_ftruncate_djgpp +#elif defined(__AMIGA__) +#define toolx_ftruncate(f, o) ftruncate(f, (off_t)(o)) #else #define toolx_ftruncate ftruncate #endif diff --git a/src/tool_ssls.c b/src/tool_ssls.c index a492d8e1e3d9..e9c486435d90 100644 --- a/src/tool_ssls.c +++ b/src/tool_ssls.c @@ -85,7 +85,7 @@ CURLcode tool_ssls_load(struct OperationConfig *config, c = memchr(line, ':', strlen(line)); if(!c) { - warnf("unrecognized line %d in ssl session file %s", i, filename); + warnf("unrecognized line %d in SSL session file %s", i, filename); continue; } *c = '\0'; @@ -109,7 +109,7 @@ CURLcode tool_ssls_load(struct OperationConfig *config, result = curl_easy_ssls_import(easy, NULL, shmac, shmac_len, sdata, sdata_len); if(result) { - warnf("import of session from line %d rejected(%d)", i, result); + warnf("import of session from line %d rejected(%d)", i, (int)result); continue; } ++imported; @@ -179,7 +179,7 @@ static CURLcode tool_ssls_exp(CURL *easy, void *userptr, out: if(result) warnf("Warning: error saving SSL session for '%s': %d", session_key, - result); + (int)result); curlx_free(enc); return result; } diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index ad1c8087da44..92bf9c43a5e6 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -56,6 +56,7 @@ static CURLcode glob_fixed(struct URLGlob *glob, char *fixed, size_t len) pat->c.set.palloc = 1; pat->c.set.size = 1; + pat->name = NULL; /* unnamed */ return CURLE_OK; } @@ -89,7 +90,7 @@ static int multiply(curl_off_t *amount, curl_off_t with) static CURLcode glob_set(struct URLGlob *glob, const char **patternp, size_t *posp, curl_off_t *amount, - int globindex) + int globindex, const struct Curl_str *name) { /* processes a set expression with the point behind the opening '{' ','-separated elements are collected until the next closing '}' @@ -98,16 +99,16 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp, bool done = FALSE; const char *pattern = *patternp; const char *opattern = pattern; - size_t opos = *posp - 1; CURLcode result = CURLE_OK; size_t size = 0; char **elem = NULL; size_t palloc = 0; /* start with this */ + DEBUGASSERT(name); while(!done) { switch(*pattern) { case '\0': /* URL ended while set was still open */ - result = globerror(glob, "unmatched brace", opos, CURLE_URL_MALFORMAT); + result = globerror(glob, "unmatched brace", *posp, CURLE_URL_MALFORMAT); goto error; case '{': @@ -198,6 +199,15 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp, pat->c.set.size = size; pat->c.set.idx = 0; pat->c.set.palloc = palloc; + if(curlx_strlen(name)) { + pat->name = curlx_memdup0(curlx_str(name), curlx_strlen(name)); + if(!pat->name) { + result = CURLE_OUT_OF_MEMORY; + goto error; + } + } + else + pat->name = NULL; /* no name */ return CURLE_OK; error: @@ -212,7 +222,7 @@ static CURLcode glob_set(struct URLGlob *glob, const char **patternp, static CURLcode glob_range(struct URLGlob *glob, const char **patternp, size_t *posp, curl_off_t *amount, - int globindex) + int globindex, const struct Curl_str *name) { /* processes a range expression with the point behind the opening '[' - char range: e.g. "a-z]", "B-Q]" @@ -224,8 +234,10 @@ static CURLcode glob_range(struct URLGlob *glob, const char **patternp, const char *pattern = *patternp; const char *c; + DEBUGASSERT(name); pat = &glob->pattern[glob->pnum]; pat->globindex = globindex; + pat->name = NULL; /* no name (so far) */ if(ISALPHA(*pattern)) { /* character range detected */ @@ -324,8 +336,10 @@ static CURLcode glob_range(struct URLGlob *glob, const char **patternp, /* the pattern is not well-formed */ return globerror(glob, "bad range", *posp, CURLE_URL_MALFORMAT); - /* typecasting to ints are fine here since we make sure above that we - are within 31 bits */ + if((CURL_OFF_T_MAX - step_n) < max_n) + return globerror(glob, "range end/step overflow", *posp, + CURLE_URL_MALFORMAT); + pat->c.num.idx = pat->c.num.min = min_n; pat->c.num.max = max_n; pat->c.num.step = step_n; @@ -338,6 +352,11 @@ static CURLcode glob_range(struct URLGlob *glob, const char **patternp, return globerror(glob, "bad range specification", *posp, CURLE_URL_MALFORMAT); + if(curlx_strlen(name)) { + pat->name = curlx_memdup0(curlx_str(name), curlx_strlen(name)); + if(!pat->name) + return CURLE_OUT_OF_MEMORY; + } *patternp = pattern; return CURLE_OK; } @@ -405,12 +424,31 @@ static CURLcode add_glob(struct URLGlob *glob, size_t pos) return CURLE_OK; } +/* returns the named glob pattern (case sensitively) if it exists, otherwise + NULL +*/ +static struct URLPattern *glob_find_name(struct URLGlob *glob, + struct Curl_str *name) +{ + size_t i; + /* find the correct glob entry */ + for(i = 0; i < glob->pnum; i++) { + if(glob->pattern[i].name && + curlx_str_cmp(name, glob->pattern[i].name)) + return &glob->pattern[i]; + } + return NULL; /* no match */ +} + +#define MAX_GLOBNAME_LEN 64 + static CURLcode glob_parse(struct URLGlob *glob, const char *pattern, size_t pos, curl_off_t *amount) { /* processes a literal string component of a URL special characters '{' and '[' branch to set/range processing functions */ + const char *ipattern = pattern; /* start position */ CURLcode result = CURLE_OK; int globindex = 0; /* count "actual" globs */ @@ -462,21 +500,40 @@ static CURLcode glob_parse(struct URLGlob *glob, const char *pattern, curlx_dyn_reset(&glob->buf); } else { + struct Curl_str name; if(!*pattern) /* done */ break; - else if(*pattern == '{') { - /* process set pattern */ + else if((*pattern == '{') || (*pattern == '[')) { + bool set = (*pattern == '{'); + const char *start; pattern++; pos++; - result = glob_set(glob, &pattern, &pos, amount, globindex++); - if(!result) - result = add_glob(glob, pos); - } - else if(*pattern == '[') { - /* process range pattern */ - pattern++; - pos++; - result = glob_range(glob, &pattern, &pos, amount, globindex++); + start = pattern; + /* fetch the name, if provided */ + if(curlx_str_single(&pattern, '<') || + curlx_str_until(&pattern, &name, MAX_GLOBNAME_LEN, '>') || + curlx_str_single(&pattern, '>')) { + /* Not a proper name. This is not reporting errors on syntax errors + on purpose: it means that if there is an existing use case that + uses what looks like a broken named-glob syntax (now introduced) + we let that function like before. */ + curlx_str_init(&name); + pattern = start; /* reset any partial patch */ + } + else { + /* check that the name is not already used */ + struct URLPattern *p = glob_find_name(glob, &name); + if(p) + return globerror(glob, "Duplicate glob name", pattern - ipattern, + CURLE_URL_MALFORMAT); + pos += (pattern - start); + } + if(set) + result = glob_set(glob, &pattern, &pos, amount, globindex++, &name); + else + result = glob_range(glob, &pattern, &pos, amount, globindex++, + &name); + if(!result) result = add_glob(glob, pos); } @@ -490,6 +547,26 @@ bool glob_inuse(struct URLGlob *glob) return glob->palloc ? TRUE : FALSE; } +/* a glob error has been confirmed, this outputs details about it to the set + error stream */ +void glob_show_error(struct URLGlob *glob, const char *url, FILE *error, + CURLcode result) +{ + char text[512]; + const char *t; + if(glob->pos) { + curl_msnprintf(text, sizeof(text), "%s in position %zu:\n%s\n%*s^", + glob->error, + glob->pos, url, (int)glob->pos - 1, " "); + t = text; + } + else + t = glob->error; + + /* send error description to the error-stream */ + curl_mfprintf(error, "curl: (%d) %s\n", (int)result, t); +} + CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum, FILE *error) { @@ -509,21 +586,8 @@ CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum, result = glob_parse(glob, url, 1, &amount); if(result) { - if(error && glob->error) { - char text[512]; - const char *t; - if(glob->pos) { - curl_msnprintf(text, sizeof(text), "%s in URL position %zu:\n%s\n%*s^", - glob->error, - glob->pos, url, (int)glob->pos - 1, " "); - t = text; - } - else - t = glob->error; - - /* send error description to the error-stream */ - curl_mfprintf(error, "curl: (%d) %s\n", result, t); - } + if(error && glob->error) + glob_show_error(glob, url, error, result); *urlnum = 1; return result; } @@ -545,6 +609,7 @@ void glob_cleanup(struct URLGlob *glob) curlx_safefree(glob->pattern[i].c.set.elem[elem]); curlx_safefree(glob->pattern[i].c.set.elem); } + curlx_safefree(glob->pattern[i].name); } curlx_safefree(glob->pattern); glob->palloc = 0; @@ -638,9 +703,11 @@ CURLcode glob_next_url(char **globbed, struct URLGlob *glob) #define MAX_OUTPUT_GLOB_LENGTH (1024 * 1024) CURLcode glob_match_url(char **output, const char *filename, - struct URLGlob *glob, SANITIZEcode *sc) + struct URLGlob *glob, struct URLGlob *glob2, + SANITIZEcode *sc) { struct dynbuf dyn; + const char *ifilename = filename; *output = NULL; *sc = SANITIZE_ERR_OK; @@ -648,11 +715,11 @@ CURLcode glob_match_url(char **output, const char *filename, while(*filename) { CURLcode result = CURLE_OK; - if(*filename == '#' && ISDIGIT(filename[1])) { - const char *ptr = filename; + struct URLPattern *pat = NULL; + if(glob_inuse(glob) && *filename == '#' && ISDIGIT(filename[1])) { + /* a numbered glob reference */ + const char *ptr = filename++; curl_off_t num; - struct URLPattern *pat = NULL; - filename++; if(!curlx_str_number(&filename, &num, glob->pnum) && num) { size_t i; num--; /* make it zero based */ @@ -664,31 +731,53 @@ CURLcode glob_match_url(char **output, const char *filename, } } } - - if(pat) { - switch(pat->type) { - case GLOB_SET: - if(pat->c.set.elem) - result = curlx_dyn_add(&dyn, pat->c.set.elem[pat->c.set.idx]); - break; - case GLOB_ASCII: { - char letter = (char)pat->c.ascii.letter; - result = curlx_dyn_addn(&dyn, &letter, 1); - break; - } - case GLOB_NUM: - result = curlx_dyn_addf(&dyn, "%0*" CURL_FORMAT_CURL_OFF_T, - pat->c.num.npad, pat->c.num.idx); - break; - default: - DEBUGASSERT(0); + if(!pat) + filename = ptr; + } + else if(*filename == '#' && (filename[1] == '<')) { + /* a named glob reference */ + struct Curl_str name; + const char *ptr = filename; + filename += 2; /* pass both leading bytes */ + if(!curlx_str_until(&filename, &name, MAX_GLOBNAME_LEN, '>') && + !curlx_str_single(&filename, '>')) { + /* find the correct glob entry */ + if(glob_inuse(glob)) + pat = glob_find_name(glob, &name); + if(!pat && glob2 && glob_inuse(glob2)) + /* scan the second glob list if there is one */ + pat = glob_find_name(glob2, &name); + if(!pat) { + /* when the name is given correctly, it needs to be an existing glob + name, which makes this an error */ curlx_dyn_free(&dyn); - return CURLE_FAILED_INIT; + return globerror(glob, "no glob exists with this name", + filename - ifilename, CURLE_BAD_FUNCTION_ARGUMENT); } } - else - /* #[num] out of range, use the #[num] in the output */ - result = curlx_dyn_addn(&dyn, ptr, filename - ptr); + if(!pat) + filename = ptr; + } + if(pat) { + switch(pat->type) { + case GLOB_SET: + if(pat->c.set.elem) + result = curlx_dyn_add(&dyn, pat->c.set.elem[pat->c.set.idx]); + break; + case GLOB_ASCII: { + char letter = (char)pat->c.ascii.letter; + result = curlx_dyn_addn(&dyn, &letter, 1); + break; + } + case GLOB_NUM: + result = curlx_dyn_addf(&dyn, "%0*" CURL_FORMAT_CURL_OFF_T, + pat->c.num.npad, pat->c.num.idx); + break; + default: + DEBUGASSERT(0); + curlx_dyn_free(&dyn); + return CURLE_FAILED_INIT; + } } else result = curlx_dyn_addn(&dyn, filename++, 1); diff --git a/src/tool_urlglob.h b/src/tool_urlglob.h index ad0f144fd232..e891258aa46e 100644 --- a/src/tool_urlglob.h +++ b/src/tool_urlglob.h @@ -33,6 +33,7 @@ typedef enum { struct URLPattern { globtype type; + char *name; /* if not NULL */ int globindex; /* the number of this particular glob or -1 if not used within {} or [] */ union { @@ -71,11 +72,15 @@ struct URLGlob { size_t pos; /* column position of error or 0 */ }; +void glob_show_error(struct URLGlob *glob, const char *url, FILE *error, + CURLcode result); + CURLcode glob_url(struct URLGlob *glob, const char *url, curl_off_t *urlnum, FILE *error); CURLcode glob_next_url(char **globbed, struct URLGlob *glob); CURLcode glob_match_url(char **output, const char *filename, - struct URLGlob *glob, SANITIZEcode *sc); + struct URLGlob *glob, struct URLGlob *glob2, + SANITIZEcode *sc); void glob_cleanup(struct URLGlob *glob); bool glob_inuse(struct URLGlob *glob); diff --git a/src/tool_vms.c b/src/tool_vms.c index 74eb210ab546..ced0efe2ed15 100644 --- a/src/tool_vms.c +++ b/src/tool_vms.c @@ -59,7 +59,7 @@ int is_vms_shell(void) } /* Have to make sure some one did not set shell to DCL */ - if(strcmp(shell, "DCL") == 0) { + if(!strcmp(shell, "DCL")) { vms_shell = 1; return 1; } @@ -154,7 +154,7 @@ static void decc_init(void) decc_init_done = 1; /* Loop through all items in the decc_feat_array[]. */ - for(i = 0; decc_feat_array[i].name != NULL; i++) { + for(i = 0; decc_feat_array[i].name; i++) { /* Get the feature index. */ feat_index = decc$feature_get_index(decc_feat_array[i].name); diff --git a/src/tool_writeout.c b/src/tool_writeout.c index ad8c77c962e9..ac8d5434b8d4 100644 --- a/src/tool_writeout.c +++ b/src/tool_writeout.c @@ -45,7 +45,7 @@ static int writeTime(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { - bool valid = false; + bool valid = FALSE; curl_off_t us = 0; (void)per; @@ -54,7 +54,7 @@ static int writeTime(FILE *stream, const struct writeoutvar *wovar, if(wovar->ci) { if(!curl_easy_getinfo(per->curl, wovar->ci, &us)) - valid = true; + valid = TRUE; } else { DEBUGASSERT(0); @@ -173,7 +173,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { - bool valid = false; + bool valid = FALSE; const char *strinfo = NULL; const char *freestr = NULL; struct dynbuf buf; @@ -189,7 +189,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, while(m->str) { if(m->num == version) { strinfo = m->str; - valid = true; + valid = TRUE; break; } m++; @@ -198,7 +198,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, } else { if(!curl_easy_getinfo(per->curl, wovar->ci, &strinfo) && strinfo) - valid = true; + valid = TRUE; } } else { @@ -243,7 +243,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, if(!strinfo) /* maybe not a TLS protocol */ strinfo = ""; - valid = true; + valid = TRUE; } } else @@ -253,19 +253,19 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, if(per_result) { strinfo = (per->errorbuffer[0]) ? per->errorbuffer : curl_easy_strerror(per_result); - valid = true; + valid = TRUE; } break; case VAR_EFFECTIVE_FILENAME: if(per->outs.filename) { strinfo = per->outs.filename; - valid = true; + valid = TRUE; } break; case VAR_INPUT_URL: if(per->url) { strinfo = per->url; - valid = true; + valid = TRUE; } break; case VAR_INPUT_URLSCHEME: @@ -291,7 +291,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar, if(per->url) { if(!urlpart(per, wovar->id, &strinfo)) { freestr = strinfo; - valid = true; + valid = TRUE; } } break; @@ -324,33 +324,33 @@ static int writeLong(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { - bool valid = false; + bool valid = FALSE; long longinfo = 0; DEBUGASSERT(wovar->writefunc == writeLong); if(wovar->ci) { if(!curl_easy_getinfo(per->curl, wovar->ci, &longinfo)) - valid = true; + valid = TRUE; } else { switch(wovar->id) { case VAR_NUM_RETRY: longinfo = per->num_retries; - valid = true; + valid = TRUE; break; case VAR_NUM_CERTS: certinfo(per); longinfo = per->certinfo ? per->certinfo->num_of_certs : 0; - valid = true; + valid = TRUE; break; case VAR_NUM_HEADERS: longinfo = per->num_headers; - valid = true; + valid = TRUE; break; case VAR_EXITCODE: longinfo = (long)per_result; - valid = true; + valid = TRUE; break; default: DEBUGASSERT(0); @@ -380,7 +380,7 @@ static int writeOffset(FILE *stream, const struct writeoutvar *wovar, struct per_transfer *per, CURLcode per_result, bool use_json) { - bool valid = false; + bool valid = FALSE; curl_off_t offinfo = 0; (void)per; @@ -389,14 +389,14 @@ static int writeOffset(FILE *stream, const struct writeoutvar *wovar, if(wovar->ci) { if(!curl_easy_getinfo(per->curl, wovar->ci, &offinfo)) - valid = true; + valid = TRUE; } else { switch(wovar->id) { case VAR_URLNUM: if(per->urlnum <= INT_MAX) { offinfo = per->urlnum; - valid = true; + valid = TRUE; } break; default: @@ -562,18 +562,39 @@ static const char *outtime(const char *ptr, /* %time{ ... */ vlen = end - ptr; curlx_dyn_init(&format, 1024); - /* insert sub-seconds for %f */ - /* insert +0000 for %z because it is otherwise not portable */ - /* insert UTC for %Z because it is otherwise not portable */ + /* Insert: + - sub-seconds for %f + - epoch seconds for %s; strftime %s uses mktime() and assumes + local time, which breaks UTC output on non-UTC hosts + - +0000 for %z because it is otherwise not portable + - UTC for %Z because it is otherwise not portable + - Keep '%%' as-is so that strftime() makes a single % out of them + */ for(i = 0; !result && i < vlen; i++) { - if((i < vlen - 1) && ptr[i] == '%' && - ((ptr[i + 1] == 'f') || ((ptr[i + 1] | 0x20) == 'z'))) { - if(ptr[i + 1] == 'f') + if((i < vlen - 1) && ptr[i] == '%') { + switch(ptr[i + 1]) { + case 'f': result = curlx_dyn_addf(&format, "%06u", usecs); - else if(ptr[i + 1] == 'Z') + break; + case 's': { + /* time_t might be either 32 or 64 bits big */ + curl_off_t tsecs = secs; + result = curlx_dyn_addf(&format, "%" CURL_FORMAT_CURL_OFF_T, tsecs); + break; + } + case 'Z': result = curlx_dyn_addn(&format, "UTC", 3); - else + break; + case 'z': result = curlx_dyn_addn(&format, "+0000", 5); + break; + case '%': + result = curlx_dyn_addn(&format, "%%", 2); + break; + default: + result = curlx_dyn_addn(&format, &ptr[i], 1); + continue; + } i++; } else @@ -787,7 +808,7 @@ void ourWriteOut(struct OperationConfig *config, struct per_transfer *per, headerJSON(stream, per); break; default: - (void)wv->writefunc(stream, wv, per, per_result, false); + (void)wv->writefunc(stream, wv, per, per_result, FALSE); break; } } diff --git a/src/tool_writeout_json.c b/src/tool_writeout_json.c index 31f528ac4710..7ea18860a5b1 100644 --- a/src/tool_writeout_json.c +++ b/src/tool_writeout_json.c @@ -105,7 +105,7 @@ void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[], for(i = 0; i < nentries; i++) { if(mappings[i].writefunc && - mappings[i].writefunc(stream, &mappings[i], per, per_result, true)) + mappings[i].writefunc(stream, &mappings[i], per, per_result, TRUE)) fputs(",", stream); } diff --git a/src/var.c b/src/var.c index 79ff888dbb99..8f9dbbb5b072 100644 --- a/src/var.c +++ b/src/var.c @@ -78,7 +78,7 @@ static ParameterError varfunc(char *c, /* content */ size_t flen, /* function string length */ struct dynbuf *out) { - bool alloc = FALSE; + char *allocptr = NULL; ParameterError err = PARAM_OK; const char *finput = f; @@ -185,19 +185,18 @@ static ParameterError varfunc(char *c, /* content */ err = PARAM_EXPAND_ERROR; break; } - if(alloc) - curlx_free(c); + if(allocptr) + curlx_free(allocptr); clen = curlx_dyn_len(out); - c = curlx_memdup0(curlx_dyn_ptr(out), clen); + allocptr = c = curlx_memdup0(curlx_dyn_ptr(out), clen); if(!c) { err = PARAM_NO_MEM; break; } - alloc = TRUE; } - if(alloc) - curlx_free(c); + if(allocptr) + curlx_free(allocptr); if(err) curlx_dyn_free(out); return err; @@ -317,7 +316,7 @@ ParameterError varexpand(const char *line, struct dynbuf *out, bool *replaced) if(result) return PARAM_NO_MEM; - added = true; + added = TRUE; } } line = &clp[2]; @@ -355,7 +354,7 @@ static ParameterError addvariable(const char *name, p = curlx_calloc(1, sizeof(struct tool_var) + nlen); if(p) { memcpy(p->name, name, nlen); - /* the null termination byte is already present from above */ + /* the null-termination byte is already present from above */ p->content = contalloc ? content : curlx_memdup0(content, clen); if(p->content) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9835bcec725f..6de7f96d0a26 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -82,8 +82,8 @@ function(curl_add_runtests _targetname _test_flags) if(_setenvs) set(_setenvs "${CMAKE_COMMAND}" -E env ${_setenvs}) endif() - # Use a special '$TFLAGS' placeholder as last argument which will be - # replaced by the contents of the environment variable in runtests.pl. + # Use a special '$TFLAGS' placeholder as last argument which is replaced + # by the contents of the environment variable in runtests.pl. # This is a workaround for CMake's limitation where commands executed by # 'make' or 'ninja' cannot portably reference environment variables. string(REPLACE " " ";" _test_flags_list "${_test_flags}") @@ -110,7 +110,7 @@ function(curl_add_pytests _targetname _test_flags) endif() string(REPLACE " " ";" _test_flags_list "${_test_flags}") add_custom_target(${_targetname} - COMMAND pytest ${_test_flags_list} "${CMAKE_CURRENT_SOURCE_DIR}/http" + COMMAND pytest ${_test_flags_list} "${CMAKE_CURRENT_SOURCE_DIR}/http${_CURL_PYTEST}" DEPENDS "${_depends}" VERBATIM USES_TERMINAL ) diff --git a/tests/allversions.pm b/tests/allversions.pm index 8ae47fc59988..0dba43e0fed4 100644 --- a/tests/allversions.pm +++ b/tests/allversions.pm @@ -32,7 +32,7 @@ our %pastversion; sub allversions { my ($file) = @_; - open(A, "<$file") || + open(A, "<$file") or die "cannot open the versions file $file\n"; my $before = 1; my $relcount; @@ -42,7 +42,7 @@ sub allversions { } elsif(!$before && /^- ([0-9.]+): (.*)/) { - $pastversion{$1}=$2; + $pastversion{$1} = $2; $relcount++; } } diff --git a/tests/appveyor.pm b/tests/appveyor.pm index f332bc2b277a..b3f8df9b3175 100644 --- a/tests/appveyor.pm +++ b/tests/appveyor.pm @@ -32,9 +32,9 @@ BEGIN { use base qw(Exporter); our @EXPORT = qw( - appveyor_check_environment - appveyor_create_test_result - appveyor_update_test_result + appveyor_check_environment + appveyor_create_test_result + appveyor_update_test_result ); } @@ -48,12 +48,12 @@ sub appveyor_check_environment { } sub appveyor_create_test_result { - my ($curl, $testnum, $testname)=@_; + my ($curl, $testnum, $testname) = @_; $testname =~ s/\\/\\\\/g; $testname =~ s/\"/\\\"/g; $testname =~ s/\'/'"'"'/g; - my $appveyor_baseurl="$ENV{'APPVEYOR_API_URL'}"; - my $appveyor_result=`$curl --silent --noproxy '*' \\ + my $appveyor_baseurl = $ENV{'APPVEYOR_API_URL'}; + my $appveyor_result = qx($curl --silent --noproxy '*' \\ --header 'Content-Type: application/json' \\ --data ' { @@ -63,14 +63,14 @@ sub appveyor_create_test_result { "outcome": "Running" } ' \\ - '$appveyor_baseurl/api/tests'`; + '$appveyor_baseurl/api/tests'); print "AppVeyor API result: $appveyor_result\n" if($appveyor_result); - $APPVEYOR_TEST_NAMES{$testnum}=$testname; + $APPVEYOR_TEST_NAMES{$testnum} = $testname; } sub appveyor_update_test_result { - my ($curl, $testnum, $error, $start, $stop)=@_; - my $testname=$APPVEYOR_TEST_NAMES{$testnum}; + my ($curl, $testnum, $error, $start, $stop) = @_; + my $testname = $APPVEYOR_TEST_NAMES{$testnum}; if(!defined $testname) { return; } @@ -96,8 +96,8 @@ sub appveyor_update_test_result { $appveyor_outcome = 'Failed'; $appveyor_category = 'Error'; } - my $appveyor_baseurl="$ENV{'APPVEYOR_API_URL'}"; - my $appveyor_result=`$curl --silent --noproxy '*' --request PUT \\ + my $appveyor_baseurl = $ENV{'APPVEYOR_API_URL'}; + my $appveyor_result = qx($curl --silent --noproxy '*' --request PUT \\ --header 'Content-Type: application/json' \\ --data ' { @@ -109,10 +109,10 @@ sub appveyor_update_test_result { "ErrorMessage": "Test $testnum $appveyor_outcome" } ' \\ - '$appveyor_baseurl/api/tests'`; + '$appveyor_baseurl/api/tests'); print "AppVeyor API result: $appveyor_result\n" if($appveyor_result); if($appveyor_category eq 'Error') { - $appveyor_result=`$curl --silent --noproxy '*' \\ + $appveyor_result = qx($curl --silent --noproxy '*' \\ --header 'Content-Type: application/json' \\ --data ' { @@ -121,7 +121,7 @@ sub appveyor_update_test_result { "details": "Test $testnum $appveyor_outcome" } ' \\ - '$appveyor_baseurl/api/build/messages'`; + '$appveyor_baseurl/api/build/messages'); print "AppVeyor API result: $appveyor_result\n" if($appveyor_result); } } diff --git a/tests/azure.pm b/tests/azure.pm index 2810f48e1777..e449a07fd485 100644 --- a/tests/azure.pm +++ b/tests/azure.pm @@ -53,9 +53,9 @@ sub azure_check_environment { } sub azure_create_test_run { - my ($curl)=@_; - my $azure_baseurl="$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; - my $azure_run=`$curl --silent --noproxy "*" \\ + my ($curl) = @_; + my $azure_baseurl = "$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; + my $azure_run = qx($curl --silent --noproxy "*" \\ --header "Authorization: Bearer $ENV{'AZURE_ACCESS_TOKEN'}" \\ --header "Content-Type: application/json" \\ --data " @@ -65,7 +65,7 @@ sub azure_create_test_run { 'build': {'id': '$ENV{'BUILD_BUILDID'}'} } " \\ - "$azure_baseurl/_apis/test/runs?api-version=5.1"`; + "$azure_baseurl/_apis/test/runs?api-version=5.1"); if($azure_run =~ /"id":(\d+)/) { return $1; } @@ -73,13 +73,13 @@ sub azure_create_test_run { } sub azure_create_test_result { - my ($curl, $azure_run_id, $testnum, $testname)=@_; + my ($curl, $azure_run_id, $testnum, $testname) = @_; $testname =~ s/\\/\\\\/g; $testname =~ s/\"/\\\"/g; $testname =~ s/\'/'"'"'/g; - my $title_testnum=sprintf("%04d", $testnum); - my $azure_baseurl="$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; - my $azure_result=`$curl --silent --noproxy '*' \\ + my $title_testnum = sprintf("%04d", $testnum); + my $azure_baseurl = "$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; + my $azure_result = qx($curl --silent --noproxy '*' \\ --header "Authorization: Bearer $ENV{'AZURE_ACCESS_TOKEN'}" \\ --header 'Content-Type: application/json' \\ --data ' @@ -94,7 +94,7 @@ sub azure_create_test_result { } ] ' \\ - '$azure_baseurl/_apis/test/runs/$azure_run_id/results?api-version=5.1'`; + '$azure_baseurl/_apis/test/runs/$azure_run_id/results?api-version=5.1'); if($azure_result =~ /\[\{"id":(\d+)/) { return $1; } @@ -102,7 +102,7 @@ sub azure_create_test_result { } sub azure_update_test_result { - my ($curl, $azure_run_id, $azure_result_id, $testnum, $error, $start, $stop)=@_; + my ($curl, $azure_run_id, $azure_result_id, $testnum, $error, $start, $stop) = @_; if(!defined $stop) { $stop = $start; } @@ -122,8 +122,8 @@ sub azure_update_test_result { else { $azure_outcome = 'Failed'; } - my $azure_baseurl="$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; - my $azure_result=`$curl --silent --noproxy '*' --request PATCH \\ + my $azure_baseurl = "$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; + my $azure_result = qx($curl --silent --noproxy '*' --request PATCH \\ --header "Authorization: Bearer $ENV{'AZURE_ACCESS_TOKEN'}" \\ --header "Content-Type: application/json" \\ --data ' @@ -137,7 +137,7 @@ sub azure_update_test_result { } ] ' \\ - '$azure_baseurl/_apis/test/runs/$azure_run_id/results?api-version=5.1'`; + '$azure_baseurl/_apis/test/runs/$azure_run_id/results?api-version=5.1'); if($azure_result =~ /\[\{"id":(\d+)/) { return $1; } @@ -145,9 +145,9 @@ sub azure_update_test_result { } sub azure_update_test_run { - my ($curl, $azure_run_id)=@_; - my $azure_baseurl="$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; - my $azure_run=`$curl --silent --noproxy '*' --request PATCH \\ + my ($curl, $azure_run_id) = @_; + my $azure_baseurl = "$ENV{'SYSTEM_TEAMFOUNDATIONCOLLECTIONURI'}$ENV{'SYSTEM_TEAMPROJECTID'}"; + my $azure_run = qx($curl --silent --noproxy '*' --request PATCH \\ --header "Authorization: Bearer $ENV{'AZURE_ACCESS_TOKEN'}" \\ --header 'Content-Type: application/json' \\ --data ' @@ -155,7 +155,7 @@ sub azure_update_test_run { "state": "Completed" } ' \\ - '$azure_baseurl/_apis/test/runs/$azure_run_id?api-version=5.1'`; + '$azure_baseurl/_apis/test/runs/$azure_run_id?api-version=5.1'); if($azure_run =~ /"id":(\d+)/) { return $1; } diff --git a/tests/certs/Makefile.am b/tests/certs/Makefile.am index d28b1674da3d..078b4490b6e2 100644 --- a/tests/certs/Makefile.am +++ b/tests/certs/Makefile.am @@ -35,7 +35,7 @@ all-am: test-ca.cacert # Rebuild the certificates -# Generate all certs in a single shot, but declare just a single target file +# Generate all certs in a single shot, but declare only a single target file # to support GNU Make <4.3 without the "grouped explicit targets" feature. test-ca.cacert: $(CERTCONFIG_CA) $(CERTCONFIGS) genserv.pl @PERL@ $(srcdir)/genserv.pl test $(CERTCONFIGS) diff --git a/tests/certs/genserv.pl b/tests/certs/genserv.pl index 96e77449aaa0..6e8ceec315ee 100755 --- a/tests/certs/genserv.pl +++ b/tests/certs/genserv.pl @@ -52,7 +52,7 @@ sub opensslfail { sub redir { my $outfn = shift if($_[0] =~ /^>/); my $hideerr = shift if($_[0] =~ /^2>/); - open(my $outfd, $outfn) || die if($outfn); + open(my $outfd, $outfn) or die if($outfn); my $pid = open3(my $in, my $out, my $err = gensym, @_); if(!$hideerr) { while(<$err>) { print STDERR $_; }; } if($outfn) { while(<$out>) { print $outfd $_; }; close($outfd); } diff --git a/tests/data/DISABLED b/tests/data/DISABLED index 8607866fd039..60dca3ca6a64 100644 --- a/tests/data/DISABLED +++ b/tests/data/DISABLED @@ -23,12 +23,12 @@ ########################################################################### # # This file can be used to specify test cases that should not run when all -# test cases are run by runtests.pl. Just add the plain test case numbers, one +# test cases are run by runtests.pl. Add the plain test case numbers, one # per line. # Lines starting with '#' letters are treated as comments. # # Uses SRP to "a server not supporting it" but modern stunnel versions -# will silently accept it and remain happy +# silently accept it and remain happy 323 # 594 @@ -43,7 +43,7 @@ 1184 1209 1211 -# fnmatch differences are just too common to make testing them sensible +# fnmatch differences are too common to make testing them sensible 1307 1316 1512 diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 5a517df9f3c9..9fb1720f7c9d 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -70,7 +70,7 @@ test370 test371 test372 test373 test374 test375 test376 test378 test379 \ test380 test381 test383 test384 test385 test386 test387 test388 test389 \ test390 test391 test392 test393 test394 test395 test396 test397 test398 \ test399 test400 test401 test402 test403 test404 test405 test406 test407 \ -test408 test409 test410 test411 test412 test413 test414 test415 test416 \ +test408 test410 test411 test412 test413 test414 test415 test416 \ test417 test418 test419 test420 test421 test422 test423 test424 test425 \ test426 test427 test428 test429 test430 test431 test432 test433 test434 \ test435 test436 test437 test438 test439 test440 test441 test442 test443 \ @@ -159,114 +159,111 @@ test1157 test1158 test1159 test1160 test1161 test1162 test1163 test1164 \ test1165 test1166 test1167 test1168 test1169 test1170 test1171 test1172 \ test1173 test1174 test1175 test1176 test1177 test1178 test1179 test1180 \ test1181 test1182 test1183 test1184 test1185 test1186 test1187 test1188 \ -test1189 test1190 test1190 test1191 test1192 test1193 test1194 test1195 \ -test1196 test1197 test1198 test1199 test1200 test1201 test1202 test1203 \ -test1204 test1205 test1206 test1207 test1208 test1209 test1210 test1211 \ -test1212 test1213 test1214 test1215 test1216 test1217 test1218 test1219 \ -test1220 test1221 test1222 test1223 test1224 test1225 test1226 test1227 \ -test1228 test1229 test1230 test1231 test1232 test1233 test1234 test1235 \ -test1236 test1237 test1238 test1239 test1240 test1241 test1242 test1243 \ -test1244 test1245 test1246 test1247 test1248 test1249 test1250 test1251 \ -test1252 test1253 test1254 test1255 test1256 test1257 test1258 test1259 \ -test1260 test1261 test1262 test1263 test1264 test1265 test1266 test1267 \ -test1268 test1269 test1270 test1271 test1272 test1273 test1274 test1275 \ -test1276 test1277 test1278 test1279 test1280 test1281 test1282 test1283 \ -test1284 test1285 test1286 test1287 test1288 test1289 test1290 test1291 \ -test1292 test1293 test1294 test1295 test1296 test1297 test1298 test1299 \ -test1300 test1301 test1302 test1303 test1304 test1305 test1306 test1307 \ -test1308 test1309 test1310 test1311 test1312 test1313 test1314 test1315 \ -test1316 test1317 test1318 test1319 test1320 test1321 test1322 test1323 \ -test1324 test1325 test1326 test1327 test1328 test1329 test1330 test1331 \ -test1332 test1333 test1334 test1335 test1336 test1337 test1338 test1339 \ -test1340 test1341 test1342 test1343 test1344 test1345 test1346 test1347 \ -test1348 test1349 test1350 test1351 test1352 test1353 test1354 test1355 \ -test1356 test1357 test1358 test1359 test1360 test1361 test1362 test1363 \ -test1364 test1365 test1366 test1367 test1368 test1369 test1370 test1371 \ -test1372 test1373 test1374 test1375 test1376 test1377 test1378 test1379 \ -test1380 test1381 test1382 test1383 test1384 test1385 test1386 test1387 \ -test1388 test1389 test1390 test1391 test1392 test1393 test1394 test1395 \ -test1396 test1397 test1398 test1399 test1400 test1401 test1402 test1403 \ -test1404 test1405 test1406 test1407 test1408 test1409 test1410 test1411 \ -test1412 test1413 test1414 test1415 test1416 test1417 test1418 test1419 \ -test1420 test1421 test1422 test1423 test1424 test1425 test1426 test1427 \ -test1428 test1429 test1430 test1431 test1432 test1433 test1434 test1435 \ -test1436 test1437 test1438 test1439 test1440 test1441 test1442 test1443 \ -test1444 test1445 test1446 test1447 test1448 test1449 test1450 test1451 \ -test1452 test1453 test1454 test1455 test1456 test1457 test1458 test1459 \ -test1460 test1461 test1462 test1463 test1464 test1465 test1466 test1467 \ -test1468 test1469 test1470 test1471 test1472 test1473 test1474 test1475 \ -test1476 test1477 test1478 test1479 test1480 test1481 test1482 test1483 \ -test1484 test1485 test1486 test1487 test1488 test1489 test1490 test1491 \ -test1492 test1493 test1494 test1495 test1496 test1497 test1498 test1499 \ -test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \ -test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \ -test1516 test1517 test1518 test1519 test1520 test1521 test1522 test1523 \ -test1524 test1525 test1526 test1527 test1528 test1529 test1530 test1531 \ -test1532 test1533 test1534 test1535 test1536 test1537 test1538 test1539 \ -test1540 test1541 test1542 test1543 test1544 test1545 test1546 test1547 \ -test1548 test1549 test1550 test1551 test1552 test1553 test1554 test1555 \ -test1556 test1557 test1558 test1559 test1560 test1561 test1562 test1563 \ -test1564 test1565 test1566 test1567 test1568 test1569 test1570 test1571 \ -test1572 test1573 test1574 test1575 test1576 test1577 test1578 test1579 \ -test1580 test1581 test1582 test1583 test1584 test1585 test1586 test1587 \ -test1588 \ -\ -test1590 test1591 test1592 test1593 test1594 test1595 test1596 test1597 \ -test1598 test1599 test1600 test1601 test1602 test1603 test1604 test1605 \ -test1606 test1607 test1608 test1609 test1610 test1611 test1612 test1613 \ -test1614 test1615 test1616 test1617 test1618 test1619 test1620 test1621 \ -test1622 test1623 test1624 test1625 test1626 test1627 \ -\ -test1630 test1631 test1632 test1633 test1634 test1635 test1636 test1637 \ -test1638 test1639 test1640 test1641 test1642 test1643 test1644 \ -\ -test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ -test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ -test1666 test1667 test1668 test1669 \ -\ -test1670 test1671 test1672 test1673 test1674 test1675 \ -\ -test1680 test1681 test1682 test1683 test1684 test1685 \ +test1189 test1190 test1191 test1192 test1193 test1194 test1195 test1196 \ +test1197 test1198 test1199 test1200 test1201 test1202 test1203 test1204 \ +test1205 test1206 test1207 test1208 test1209 test1210 test1211 test1212 \ +test1213 test1214 test1215 test1216 test1217 test1218 test1219 test1220 \ +test1221 test1222 test1223 test1224 test1225 test1226 test1227 test1228 \ +test1229 test1230 test1231 test1232 test1233 test1234 test1235 test1236 \ +test1237 test1238 test1239 test1240 test1241 test1242 test1243 test1244 \ +test1245 test1246 test1247 test1248 test1249 test1250 test1251 test1252 \ +test1253 test1254 test1255 test1256 test1257 test1258 test1259 test1260 \ +test1261 test1262 test1263 test1264 test1265 test1266 test1267 test1268 \ +test1269 test1270 test1271 test1272 test1273 test1274 test1275 test1276 \ +test1277 test1278 test1279 test1280 test1281 test1282 test1283 test1284 \ +test1285 test1286 test1287 test1288 test1289 test1290 test1291 test1292 \ +test1293 test1294 test1295 test1296 test1297 test1298 test1299 test1300 \ +test1301 test1302 test1303 test1304 test1305 test1306 test1307 test1308 \ +test1309 test1310 test1311 test1312 test1313 test1314 test1315 test1316 \ +test1317 test1318 test1319 test1320 test1321 test1322 test1323 test1324 \ +test1325 test1326 test1327 test1328 test1329 test1330 test1331 test1332 \ +test1333 test1334 test1335 test1336 test1337 test1338 test1339 test1340 \ +test1341 test1342 test1343 test1344 test1345 test1346 test1347 test1348 \ +test1349 test1350 test1351 test1352 test1353 test1354 test1355 test1356 \ +test1357 test1358 test1359 test1360 test1361 test1362 test1363 test1364 \ +test1365 test1366 test1367 test1368 test1369 test1370 test1371 test1372 \ +test1373 test1374 test1375 test1376 test1377 test1378 test1379 test1380 \ +test1381 test1382 test1383 test1384 test1385 test1386 test1387 test1388 \ +test1389 test1390 test1391 test1392 test1393 test1394 test1395 test1396 \ +test1397 test1398 test1399 test1400 test1401 test1402 test1403 test1404 \ +test1405 test1406 test1407 test1408 test1409 test1410 test1411 test1412 \ +test1413 test1414 test1415 test1416 test1417 test1418 test1419 test1420 \ +test1421 test1422 test1423 test1424 test1425 test1426 test1427 test1428 \ +test1429 test1430 test1431 test1432 test1433 test1434 test1435 test1436 \ +test1437 test1438 test1439 test1440 test1441 test1442 test1443 test1444 \ +test1445 test1446 test1447 test1448 test1449 test1450 test1451 test1452 \ +test1453 test1454 test1455 test1456 test1457 test1458 test1459 test1460 \ +test1461 test1462 test1463 test1464 test1465 test1466 test1467 test1468 \ +test1469 test1470 test1471 test1472 test1473 test1474 test1475 test1476 \ +test1477 test1478 test1479 test1480 test1481 test1482 test1483 test1484 \ +test1485 test1486 test1487 test1488 test1489 test1490 test1491 test1492 \ +test1493 test1494 test1495 test1496 test1497 test1498 test1499 test1500 \ +test1501 test1502 test1503 test1504 test1505 test1506 test1507 test1508 \ +test1509 test1510 test1511 test1512 test1513 test1514 test1515 test1516 \ +test1517 test1518 test1519 test1520 test1521 test1522 test1523 test1524 \ +test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 \ +test1533 test1534 test1535 test1536 test1537 test1538 test1539 test1540 \ +test1541 test1542 test1543 test1544 test1545 test1546 test1547 test1548 \ +test1549 test1550 test1551 test1552 test1553 test1554 test1555 test1556 \ +test1557 test1558 test1559 test1560 test1561 test1562 test1563 test1564 \ +test1565 test1566 test1567 test1568 test1569 test1570 test1571 test1572 \ +test1573 test1574 test1575 test1576 test1577 test1578 test1579 test1580 \ +test1581 test1582 test1583 test1584 test1585 test1586 test1587 test1588 \ +test1589 test1590 test1591 test1592 test1593 test1594 test1595 test1596 \ +test1597 test1598 test1599 test1600 test1601 test1602 test1603 test1604 \ +test1605 test1606 test1607 test1608 test1610 test1611 test1612 \ +test1613 test1614 test1615 test1616 test1617 test1618 test1619 test1620 \ +test1621 test1622 test1623 test1624 test1625 test1626 test1627 test1628 \ +test1629 test1630 test1631 test1632 test1633 test1634 test1635 test1636 \ +test1637 test1638 test1639 test1640 test1641 test1642 test1643 test1644 \ +test1645 test1646 test1647 test1648 test1649 test1650 test1651 test1652 \ +test1653 test1654 test1655 test1656 test1657 test1658 test1659 test1660 \ +test1661 test1662 test1663 test1664 test1665 test1666 test1667 test1668 \ +test1669 test1670 test1671 test1672 test1673 test1674 test1675 test1676 \ +test1677 test1680 test1681 test1682 test1683 test1684 \ +test1685 test1686 \ \ test1700 test1701 test1702 test1703 test1704 test1705 test1706 test1707 \ test1708 test1709 test1710 test1711 test1712 test1713 test1714 test1715 \ -test1720 \ +test1720 test1721 test1722 test1723 test1724 test1725 \ \ test1800 test1801 test1802 test1847 test1848 test1849 test1850 test1851 \ \ test1900 test1901 test1902 test1903 test1904 test1905 test1906 test1907 \ test1908 test1909 test1910 test1911 test1912 test1913 test1914 test1915 \ -test1916 test1917 test1918 test1919 test1920 \ +test1916 test1917 test1918 test1919 test1920 test1921 test1922 \ \ test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \ test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \ test1955 test1956 test1957 test1958 test1959 test1960 test1964 test1965 \ -test1966 \ -\ -test1970 test1971 test1972 test1973 test1974 test1975 test1976 test1977 \ -test1978 test1979 test1980 test1981 test1982 test1983 test1984 \ +test1966 test1967 test1970 test1971 test1972 test1973 \ +test1974 test1975 test1976 test1977 test1978 test1979 test1980 test1981 \ +test1982 test1983 test1984 \ \ test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ -test2008 test2009 test2010 test2011 test2012 test2013 \ +test2008 test2009 test2010 test2011 test2012 test2013 test2014 test2015 \ \ test2023 \ test2024 test2025 test2026 test2027 test2028 test2029 test2030 test2031 \ -test2032 test2033 test2034 test2035 test2037 test2038 test2039 \ +test2032 test2033 test2034 test2035 test2036 test2037 test2038 test2039 \ test2040 test2041 test2042 test2043 test2044 test2045 test2046 test2047 \ test2048 test2049 test2050 test2051 test2052 test2053 test2054 test2055 \ test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 \ test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \ test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \ test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \ -test2088 test2089 test2090 test2091 \ -test2100 test2101 test2102 test2103 test2104 \ +test2088 test2089 test2090 test2091 test2092 \ +test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \ +test2108 \ \ -test2200 test2201 test2202 test2203 test2204 test2205 \ +test2200 test2201 test2202 test2203 test2204 test2205 test2206 test2207 \ +test2208 \ \ test2300 test2301 test2302 test2303 test2304 test2306 test2307 test2308 \ -test2309 \ +test2309 test2310 test2311 \ \ test2400 test2401 test2402 test2403 test2404 test2405 test2406 test2407 \ +test2408 test2409 test2410 test2411 test2412 test2413 \ \ test2500 test2501 test2502 test2503 test2504 test2505 test2506 \ \ @@ -282,13 +279,16 @@ test3016 test3017 test3018 test3019 test3020 test3021 test3022 test3023 \ test3024 test3025 test3026 test3027 test3028 test3029 test3030 test3031 \ test3032 test3033 test3034 test3035 test3036 \ \ -test3100 test3101 test3102 test3103 test3104 test3105 \ +test3100 test3101 test3102 test3103 test3104 test3105 test3106 \ \ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \ test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \ -test3216 test3217 test3218 test3219 test3220 \ +test3216 test3217 test3218 test3219 test3220 test3221 test3222 \ +\ +test3300 test3301 test3302 test3303 test3304 test3305 \ \ -test3300 test3301 \ +test3400 \ +test3401 \ \ test4000 test4001 diff --git a/tests/data/test1007 b/tests/data/test1007 index fdd6af2c9a3c..2706c049a55a 100644 --- a/tests/data/test1007 +++ b/tests/data/test1007 @@ -4,7 +4,6 @@ TFTP TFTP WRQ -FAILURE diff --git a/tests/data/test1030 b/tests/data/test1030 index ddf7370eaaf5..cb35a0f085cb 100644 --- a/tests/data/test1030 +++ b/tests/data/test1030 @@ -66,7 +66,7 @@ crypto digest -HTTP PUT with --anyauth authorization (picking Digest) +HTTP PUT with --anyauth, picking Digest. Persistent connection. http://%HOSTIP:%HTTPPORT/%TESTNUMBER -T %LOGDIR/put%TESTNUMBER -u testuser:testpass --anyauth diff --git a/tests/data/test1034 b/tests/data/test1034 index 8d0c470e51a3..be48039b54ca 100644 --- a/tests/data/test1034 +++ b/tests/data/test1034 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy IDN -FAILURE config file diff --git a/tests/data/test1035 b/tests/data/test1035 index fe8ae115b234..9139a41aa348 100644 --- a/tests/data/test1035 +++ b/tests/data/test1035 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy IDN -FAILURE diff --git a/tests/data/test1042 b/tests/data/test1042 index 66ac19f4fd74..22b1acc03e04 100644 --- a/tests/data/test1042 +++ b/tests/data/test1042 @@ -5,7 +5,6 @@ HTTP HTTP GET Resume -FAILURE diff --git a/tests/data/test1059 b/tests/data/test1059 index c982ec3ac1a7..1cb2ff836a81 100644 --- a/tests/data/test1059 +++ b/tests/data/test1059 @@ -6,7 +6,6 @@ HTTP HTTP CONNECT proxytunnel FTP -FAILURE diff --git a/tests/data/test1063 b/tests/data/test1063 index 63a2949716de..1ac641149223 100644 --- a/tests/data/test1063 +++ b/tests/data/test1063 @@ -4,7 +4,6 @@ FILE Range -FAILURE @@ -21,7 +20,7 @@ Largefile Invalid large X- range on a file:// -# This range value is 2**32+7, which will be truncated to the valid value 7 +# This range value is 2**32 + 7, which is truncated to the valid value 7 # if the large file support is not working correctly -r 4294967303- file://localhost%FILE_PWD/%LOGDIR/test%TESTNUMBER.txt diff --git a/tests/data/test1075 b/tests/data/test1075 index f5431927039b..6f2a21bba191 100644 --- a/tests/data/test1075 +++ b/tests/data/test1075 @@ -13,7 +13,7 @@ HTTP Basic auth # The test server provides no way to respond differently to a subsequent # Basic authenticated request (we really want to respond with 200 for -# the second), so just respond with 401 for both and let curl deal with it. +# the second), so respond with 401 for both and let curl deal with it. HTTP/1.1 401 Authorization Required Server: testcurl diff --git a/tests/data/test1084 b/tests/data/test1084 index 975c751b160a..66335111f899 100644 --- a/tests/data/test1084 +++ b/tests/data/test1084 @@ -5,7 +5,6 @@ HTTP HTTP GET --interface -FAILURE non-existing host diff --git a/tests/data/test1085 b/tests/data/test1085 index 8a2bbd8c9072..2b692ffee3a7 100644 --- a/tests/data/test1085 +++ b/tests/data/test1085 @@ -6,7 +6,6 @@ HTTP HTTP GET --interface IPv6 -FAILURE non-existing host diff --git a/tests/data/test1086 b/tests/data/test1086 index efb51c413bc5..440aadfa0e75 100644 --- a/tests/data/test1086 +++ b/tests/data/test1086 @@ -6,7 +6,6 @@ FTP EPSV RETR timeout -FAILURE SLOWDOWNDATA flaky timing-dependent diff --git a/tests/data/test1096 b/tests/data/test1096 index 0485f7c2e359..971aef1cb177 100644 --- a/tests/data/test1096 +++ b/tests/data/test1096 @@ -5,7 +5,6 @@ FTP PASV RETR -FAILURE # Server-side diff --git a/tests/data/test1099 b/tests/data/test1099 index 7539832ca4f9..a38038713f56 100644 --- a/tests/data/test1099 +++ b/tests/data/test1099 @@ -4,7 +4,6 @@ TFTP TFTP RRQ -FAILURE diff --git a/tests/data/test1105 b/tests/data/test1105 index 7d4df9556cde..45251d3e7ca0 100644 --- a/tests/data/test1105 +++ b/tests/data/test1105 @@ -33,7 +33,7 @@ Set-Cookie: bar=foo%TABbar http -HTTP with cookie parser and header recording +HTTP POST with cookies "http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER?parm1=this*that/other/thing%AMPparm2=foobar/%TESTNUMBER" -c %LOGDIR/cookie%TESTNUMBER.txt -d "userid=myname%AMPpassword=mypassword" diff --git a/tests/data/test111 b/tests/data/test111 index 655cc0c275af..10fbfa91cffa 100644 --- a/tests/data/test111 +++ b/tests/data/test111 @@ -5,7 +5,6 @@ FTP EPSV Resume -FAILURE # Server-side diff --git a/tests/data/test1112 b/tests/data/test1112 index 0d9e19ef80ed..45ce8e0740b7 100644 --- a/tests/data/test1112 +++ b/tests/data/test1112 @@ -7,7 +7,6 @@ FTPS EPSV RETR timeout -FAILURE SLOWDOWNDATA timing-dependent diff --git a/tests/data/test1120 b/tests/data/test1120 index 14500563344e..e5c223aa0679 100644 --- a/tests/data/test1120 +++ b/tests/data/test1120 @@ -7,7 +7,6 @@ PORT RETR 421 timeout -FAILURE # Server-side diff --git a/tests/data/test113 b/tests/data/test113 index 6a7266031d56..68637a12b816 100644 --- a/tests/data/test113 +++ b/tests/data/test113 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test1130 b/tests/data/test1130 index a23c9d314b4c..abbfc499a1fc 100644 --- a/tests/data/test1130 +++ b/tests/data/test1130 @@ -32,7 +32,7 @@ Content-Type: text/html # We use skip to make the test server never read the full payload off # the socket and instead return the response at once. In actuality, the -# long --expect100-timeout means that it will never get a chance to read this. +# long --expect100-timeout means that it never gets a chance to read this. skip: 100 diff --git a/tests/data/test1131 b/tests/data/test1131 index 85e06047412f..855121d27852 100644 --- a/tests/data/test1131 +++ b/tests/data/test1131 @@ -32,7 +32,7 @@ FAILURE2 # We use skip to make the test server never read the full payload off # the socket and instead return the response at once. In actuality, the -# long --expect100-timeout means that it will never get a chance to read this. +# long --expect100-timeout means that it never gets a chance to read this. skip: 100 diff --git a/tests/data/test114 b/tests/data/test114 index 850584479da4..6972e6a8352d 100644 --- a/tests/data/test114 +++ b/tests/data/test114 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test1144 b/tests/data/test1144 index a7baf6ef0e58..4446e63212d8 100644 --- a/tests/data/test1144 +++ b/tests/data/test1144 @@ -11,7 +11,7 @@ HTTP/0.9 # Server-side -No headers at all, just data swsclose +No headers at all, only data swsclose Let's get diff --git a/tests/data/test115 b/tests/data/test115 index 00b300a58ef7..5fe1a74a61bf 100644 --- a/tests/data/test115 +++ b/tests/data/test115 @@ -4,7 +4,6 @@ FTP PASV -FAILURE # Server-side diff --git a/tests/data/test1152 b/tests/data/test1152 index ad548e64f194..dfbde558d0af 100644 --- a/tests/data/test1152 +++ b/tests/data/test1152 @@ -3,13 +3,12 @@ FTP -FAILURE # Server-side -REPLY PWD 257 "just one +REPLY PWD 257 "Only one diff --git a/tests/data/test116 b/tests/data/test116 index fe519c5bbffc..3ce674e401f2 100644 --- a/tests/data/test116 +++ b/tests/data/test116 @@ -5,7 +5,6 @@ FTP EPRT PORT -FAILURE EPRT refused diff --git a/tests/data/test117 b/tests/data/test117 index 9142e15c2e9f..6fdbb33a582d 100644 --- a/tests/data/test117 +++ b/tests/data/test117 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test118 b/tests/data/test118 index 148e7d6e4d9e..67c17065d511 100644 --- a/tests/data/test118 +++ b/tests/data/test118 @@ -5,7 +5,6 @@ FTP PASV RETR -FAILURE # Server-side diff --git a/tests/data/test1185 b/tests/data/test1185 index 217a0a160479..f804c7ba044f 100644 --- a/tests/data/test1185 +++ b/tests/data/test1185 @@ -91,6 +91,10 @@ void startfunc(int a, int b) { int d = impl->magicbad(1); /* member function always allowed */ int e = impl.magicbad(); /* member function always allowed */ + curlx_free(ptr); /* two line + comment */ + ptr = NULL; /* comment more */ + /* comment does not end @@ -227,13 +231,16 @@ void startfunc(int a, int b) { ./%LOGDIR/code1185.c:71:2: warning: // comment (CPPCOMMENTS) // CPP comment ? ^ +./%LOGDIR/code1185.c:78:2: warning: replace curlx_free() + NULL assignment with curlx_safefree() (USESAFEFREE) + ptr = NULL; /* comment more */ + ^ ./%LOGDIR/code1185.c:1:1: error: Missing copyright statement (COPYRIGHT) %SP ^ ./%LOGDIR/code1185.c:1:1: error: Missing closing comment (OPENCOMMENT) %SP ^ -checksrc: 3 errors and 42 warnings +checksrc: 3 errors and 43 warnings 5 diff --git a/tests/data/test119 b/tests/data/test119 index 8b6ef10339f1..5f9140ff2c35 100644 --- a/tests/data/test119 +++ b/tests/data/test119 @@ -5,7 +5,6 @@ FTP PORT RETR -FAILURE # Server-side diff --git a/tests/data/test1196 b/tests/data/test1196 index dc79141cb3a5..118c52a3367f 100644 --- a/tests/data/test1196 +++ b/tests/data/test1196 @@ -28,7 +28,7 @@ mqtt mqtt -MQTT with error in CONNACK +MQTT with "unaccaptable protocol version" CONNACK mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER diff --git a/tests/data/test1208 b/tests/data/test1208 index 124419b6dc1b..8ae1e3acbe8d 100644 --- a/tests/data/test1208 +++ b/tests/data/test1208 @@ -7,7 +7,6 @@ PORT RETR NODATACONN150 timeout -FAILURE flaky timing-dependent diff --git a/tests/data/test1209 b/tests/data/test1209 index 8eb540a22c81..449e8f5fa920 100644 --- a/tests/data/test1209 +++ b/tests/data/test1209 @@ -7,7 +7,6 @@ PORT RETR NODATACONN timeout -FAILURE # Server-side diff --git a/tests/data/test1211 b/tests/data/test1211 index cb66fae8c5be..eb267d9dcbec 100644 --- a/tests/data/test1211 +++ b/tests/data/test1211 @@ -7,7 +7,6 @@ PORT RETR NODATACONN425 timeout -FAILURE # Server-side @@ -26,7 +25,7 @@ NODATACONN425 ftp -FTP PORT and 425 on download +FTP PORT and 425 on download and timeout ftp://%HOSTIP:%FTPPORT/%TESTNUMBER -P - diff --git a/tests/data/test1221 b/tests/data/test1221 index 322a5f878f4c..5ff7add9951c 100644 --- a/tests/data/test1221 +++ b/tests/data/test1221 @@ -44,7 +44,7 @@ content to _?!#$'|%LT%GT # Verify data after the test has been "shot" -POST /%TESTNUMBER?my+name+is+moo%5b%5d%AMPyes=s+i+r%AMPv_alue=content+to+_%3f%21%23%24%27%7c%3c%3e%0a%AMPcontent+to+_%3f%21%23%24%27%7c%3c%3e%0a%AMP%3d%3d HTTP/1.1 +POST /%TESTNUMBER?my+name+is+moo%5B%5D%AMPyes=s+i+r%AMPv_alue=content+to+_%3F%21%23%24%27%7C%3C%3E%0A%AMPcontent+to+_%3F%21%23%24%27%7C%3C%3E%0A%AMP%3D%3D HTTP/1.1 Host: %HOSTIP:%HTTPPORT User-Agent: curl/%VERSION Accept: */* diff --git a/tests/data/test1234 b/tests/data/test1234 index e7ffbf2eec26..bb3cde5974e4 100644 --- a/tests/data/test1234 +++ b/tests/data/test1234 @@ -4,7 +4,6 @@ globbing {} list -FAILURE # Server-side diff --git a/tests/data/test1236 b/tests/data/test1236 index 0d31ac1f4afd..2b7318516d4a 100644 --- a/tests/data/test1236 +++ b/tests/data/test1236 @@ -3,7 +3,6 @@ globbing -FAILURE # Server-side diff --git a/tests/data/test1238 b/tests/data/test1238 index a8fbc8a3f027..bb9b5d2b2b99 100644 --- a/tests/data/test1238 +++ b/tests/data/test1238 @@ -5,7 +5,6 @@ TFTP TFTP RRQ timeout -FAILURE DELAY @@ -15,7 +14,7 @@ DELAY writedelay: 2000 -# ~1200 bytes (so that they do not fit in two 512 byte chunks) +# ~1200 bytes (so that they do not fit in two 512-byte chunks) 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 diff --git a/tests/data/test125 b/tests/data/test125 index ef2591d4a71e..1f28daaf0fcc 100644 --- a/tests/data/test125 +++ b/tests/data/test125 @@ -3,7 +3,6 @@ FTP -FAILURE # Server-side diff --git a/tests/data/test1256 b/tests/data/test1256 index d35ee627e187..7db26428ec76 100644 --- a/tests/data/test1256 +++ b/tests/data/test1256 @@ -29,7 +29,7 @@ foo http -http_proxy, override NO_PROXY by --noproxy and target URL through proxy +http_proxy, override NO_PROXY by --noproxy host http_proxy=http://%HOSTIP:%HTTPPORT diff --git a/tests/data/test1257 b/tests/data/test1257 index 40f1028c5c29..fc6f56e33cbd 100644 --- a/tests/data/test1257 +++ b/tests/data/test1257 @@ -29,7 +29,7 @@ foo http -http_proxy, override NO_PROXY by --noproxy and target URL through proxy +http_proxy, override NO_PROXY by --noproxy "" http_proxy=http://%HOSTIP:%HTTPPORT diff --git a/tests/data/test1282 b/tests/data/test1282 index 06cf5170edc6..774f54cffaee 100644 --- a/tests/data/test1282 +++ b/tests/data/test1282 @@ -10,7 +10,7 @@ RETR # Server-side -REPLY PASS 633 XXXXXXXX\x00\x00XXXXXXXX +REPLY PASS 633 XXXXXXXXXXXXXXXX diff --git a/tests/data/test1349 b/tests/data/test1349 index 04a6680635a3..ae19f87a7cd4 100644 --- a/tests/data/test1349 +++ b/tests/data/test1349 @@ -47,8 +47,8 @@ fooo mooo -# The final "221 bye bye baby" response to QUIT will not be recorded -# since that is not considered part of this particular transfer! +# The final "221 bye bye baby" response to QUIT is not recorded +# since that is not considered part of this particular transfer. 220- _ _ ____ _ 220- ___| | | | _ \| | diff --git a/tests/data/test1350 b/tests/data/test1350 index 2520df1dab34..8335bda8117f 100644 --- a/tests/data/test1350 +++ b/tests/data/test1350 @@ -47,8 +47,8 @@ fooo mooo -# The final "221 bye bye baby" response to QUIT will not be recorded -# since that is not considered part of this particular transfer! +# The final "221 bye bye baby" response to QUIT is not recorded +# since that is not considered part of this particular transfer. 220- _ _ ____ _ 220- ___| | | | _ \| | diff --git a/tests/data/test1351 b/tests/data/test1351 index 8351e49c68ca..525b23fdb01c 100644 --- a/tests/data/test1351 +++ b/tests/data/test1351 @@ -48,8 +48,8 @@ fooo mooo -# The final "221 bye bye baby" response to QUIT will not be recorded -# since that is not considered part of this particular transfer! +# The final "221 bye bye baby" response to QUIT is not recorded +# since that is not considered part of this particular transfer. 220- _ _ ____ _ 220- ___| | | | _ \| | diff --git a/tests/data/test1352 b/tests/data/test1352 index 4ff3222c6180..725e63fee365 100644 --- a/tests/data/test1352 +++ b/tests/data/test1352 @@ -48,8 +48,8 @@ fooo mooo -# The final "221 bye bye baby" response to QUIT will not be recorded -# since that is not considered part of this particular transfer! +# The final "221 bye bye baby" response to QUIT is not recorded +# since that is not considered part of this particular transfer. 220- _ _ ____ _ 220- ___| | | | _ \| | diff --git a/tests/data/test1353 b/tests/data/test1353 index b2ede58f3b80..ee05e78120e3 100644 --- a/tests/data/test1353 +++ b/tests/data/test1353 @@ -47,8 +47,8 @@ fooo mooo -# The final "221 bye bye baby" response to QUIT will not be recorded -# since that is not considered part of this particular transfer! +# The final "221 bye bye baby" response to QUIT is not recorded +# since that is not considered part of this particular transfer. 220- _ _ ____ _ 220- ___| | | | _ \| | diff --git a/tests/data/test1400 b/tests/data/test1400 index cf4aabbb8710..d3279a9e7cd4 100644 --- a/tests/data/test1400 +++ b/tests/data/test1400 @@ -51,7 +51,7 @@ Accept: */* # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with # CURLOPT_INTERLEAVEDATA requires RTSP protocol -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1401 b/tests/data/test1401 index 3f84805e00a0..74d174833922 100644 --- a/tests/data/test1401 +++ b/tests/data/test1401 @@ -62,7 +62,7 @@ X-Men: cyclops, iceman # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1402 b/tests/data/test1402 index 82061a385ca7..6dd608458abc 100644 --- a/tests/data/test1402 +++ b/tests/data/test1402 @@ -54,7 +54,7 @@ foo=bar%AMPbaz=quux # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1403 b/tests/data/test1403 index c3416a330586..14383314d48e 100644 --- a/tests/data/test1403 +++ b/tests/data/test1403 @@ -51,7 +51,7 @@ Accept: */* # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1404 b/tests/data/test1404 index 9fbd55526f16..d1a1d5217c7b 100644 --- a/tests/data/test1404 +++ b/tests/data/test1404 @@ -96,7 +96,7 @@ dummy data # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1405 b/tests/data/test1405 index 853c967ba77e..611dbf720fbf 100644 --- a/tests/data/test1405 +++ b/tests/data/test1405 @@ -65,11 +65,11 @@ QUIT # CURLOPT_USERAGENT and CURLOPT_MAXREDIRS requires HTTP protocol # CURLOPT_INTERLEAVEDATA requires RTSP (HTTP) protocol -# support, IOW depends on configuration - just ignore these. +# support, IOW depends on configuration - ignore these. $_ = '' if /CURLOPT_USERAGENT/ $_ = '' if /CURLOPT_MAXREDIRS/ # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1406 b/tests/data/test1406 index 26ad1c7998fa..d7c4adcfa993 100644 --- a/tests/data/test1406 +++ b/tests/data/test1406 @@ -61,7 +61,7 @@ body . -# These options vary with configurations - just ignore them +# These options vary with configurations - ignore them # CURLOPT_INTERLEAVEDATA requires RTSP (HTTP) protocol $_ = '' if /CURLOPT_MAXREDIRS/ $_ = '' if /CURLOPT_SSL_VERIFYPEER/ diff --git a/tests/data/test1407 b/tests/data/test1407 index 1436786115db..599eb1d3e4ef 100644 --- a/tests/data/test1407 +++ b/tests/data/test1407 @@ -48,7 +48,7 @@ LIST %TESTNUMBER QUIT -# These options vary with configurations - just ignore them +# These options vary with configurations - ignore them # CURLOPT_USERAGENT and CURLOPT_MAXREDIRS requires HTTP protocol # CURLOPT_INTERLEAVEDATA requires RTSP (HTTP) protocol $_ = '' if /CURLOPT_USERAGENT/ diff --git a/tests/data/test1409 b/tests/data/test1409 index 153d4a064453..50d00e44d739 100644 --- a/tests/data/test1409 +++ b/tests/data/test1409 @@ -3,7 +3,6 @@ cmdline -FAILURE diff --git a/tests/data/test1410 b/tests/data/test1410 index 4f1b39113e4c..e5d8660e7881 100644 --- a/tests/data/test1410 +++ b/tests/data/test1410 @@ -3,7 +3,6 @@ cmdline -FAILURE diff --git a/tests/data/test1412 b/tests/data/test1412 index babb42f29b00..ba9165d9f356 100644 --- a/tests/data/test1412 +++ b/tests/data/test1412 @@ -26,7 +26,7 @@ Connection: close This is not the real page -# The second URL will get this response +# The second URL gets this response HTTP/1.1 401 Authorization Required swsclose Server: Apache/1.3.27 (Darwin) PHP/4.1.2 diff --git a/tests/data/test1420 b/tests/data/test1420 index 57fdbe18d89c..cdf51cd0cf35 100644 --- a/tests/data/test1420 +++ b/tests/data/test1420 @@ -54,7 +54,7 @@ A004 FETCH 1 BODY[] A005 LOGOUT -# These options vary with configurations - just ignore them +# These options vary with configurations - ignore them # CURLOPT_INTERLEAVEDATA requires RTSP (HTTP) protocol $_ = '' if /CURLOPT_MAXREDIRS/ $_ = '' if /CURLOPT_SSL_VERIFYPEER/ diff --git a/tests/data/test1422 b/tests/data/test1422 index d51cd6e7f529..b7f856c13ea3 100644 --- a/tests/data/test1422 +++ b/tests/data/test1422 @@ -3,6 +3,7 @@ HTTP +FILE HTTP GET -J diff --git a/tests/data/test1423 b/tests/data/test1423 index 4acc0757b27f..b42d369d7c12 100644 --- a/tests/data/test1423 +++ b/tests/data/test1423 @@ -3,6 +3,7 @@ HTTP +FILE HTTP GET diff --git a/tests/data/test1447 b/tests/data/test1447 index 4b0ed1b584af..ea68ef92300b 100644 --- a/tests/data/test1447 +++ b/tests/data/test1447 @@ -3,7 +3,6 @@ HTTP proxy -FAILURE # Server-side diff --git a/tests/data/test1453 b/tests/data/test1453 index 3b1729633505..5d2166332c46 100644 --- a/tests/data/test1453 +++ b/tests/data/test1453 @@ -3,7 +3,6 @@ Too long tftp filename -FAILURE # Server-side diff --git a/tests/data/test1465 b/tests/data/test1465 index 2c4804d5195a..d8d6115b1fde 100644 --- a/tests/data/test1465 +++ b/tests/data/test1465 @@ -58,7 +58,7 @@ Content-Type: application/x-www-form-urlencoded # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1469 b/tests/data/test1469 index 4ca24418f99b..3b742c1b2c32 100644 --- a/tests/data/test1469 +++ b/tests/data/test1469 @@ -4,7 +4,6 @@ FTP URL -FAILURE diff --git a/tests/data/test1471 b/tests/data/test1471 index ffd3ef59abab..70c454b0a45e 100644 --- a/tests/data/test1471 +++ b/tests/data/test1471 @@ -4,7 +4,6 @@ Onion Tor -FAILURE # Server-side diff --git a/tests/data/test1472 b/tests/data/test1472 index 26fb456808b6..b9e2d6dd67c0 100644 --- a/tests/data/test1472 +++ b/tests/data/test1472 @@ -4,7 +4,6 @@ Onion Tor -FAILURE # Server-side diff --git a/tests/data/test1481 b/tests/data/test1481 index 98c018bc2699..8a0d31078533 100644 --- a/tests/data/test1481 +++ b/tests/data/test1481 @@ -54,7 +54,7 @@ Proxy-Connection: Keep-Alive # CURLOPT_SSL_VERIFYPEER, SSH_KNOWNHOSTS and HTTP_VERSION vary with # CURLOPT_INTERLEAVEDATA requires RTSP protocol -# configurations - just ignore them +# configurations - ignore them $_ = '' if /CURLOPT_SSL_VERIFYPEER/ $_ = '' if /CURLOPT_SSH_KNOWNHOSTS/ $_ = '' if /CURLOPT_HTTP_VERSION/ diff --git a/tests/data/test1483 b/tests/data/test1483 index eb1931770644..c7373b2a4615 100644 --- a/tests/data/test1483 +++ b/tests/data/test1483 @@ -51,7 +51,7 @@ writedelay: 10 http -HTTP GET with double chunked in TE header +HTTP GET with two Transfer-Encoding: chunked headers http://%HOSTIP:%HTTPPORT/%TESTNUMBER -D %LOGDIR/heads%TESTNUMBER diff --git a/tests/data/test1509 b/tests/data/test1509 index afb1a6613955..7e57cd0fad9b 100644 --- a/tests/data/test1509 +++ b/tests/data/test1509 @@ -58,7 +58,7 @@ lib%TESTNUMBER proxy -simple multi http:// through proxytunnel with authentication info +http:// through proxytunnel http://the.old.moo.%TESTNUMBER:%HTTPPORT/%TESTNUMBER %HOSTIP:%PROXYPORT diff --git a/tests/data/test1513 b/tests/data/test1513 index 4b119fca7a0e..b84183846df1 100644 --- a/tests/data/test1513 +++ b/tests/data/test1513 @@ -30,7 +30,7 @@ lib%TESTNUMBER return failure immediately from progress callback -# this server/host will not be used for real +# this server/host is not used for real http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test1515 b/tests/data/test1515 index ac3ad254b85a..2ad5ac0797b8 100644 --- a/tests/data/test1515 +++ b/tests/data/test1515 @@ -5,13 +5,12 @@ HTTP multi -FAILURE resolve -# Close the connection after the first request. Second request will happen after +# Close the connection after the first request. Second request happens after # the DNS cache timeout elapses and must succeed exactly like the first one. HTTP/1.1 200 OK diff --git a/tests/data/test1516 b/tests/data/test1516 index 704a79164648..02f2c6d84f17 100644 --- a/tests/data/test1516 +++ b/tests/data/test1516 @@ -5,7 +5,6 @@ HTTP multi -FAILURE resolve diff --git a/tests/data/test1520 b/tests/data/test1520 index 07ccdf281c95..c26c08866fb2 100644 --- a/tests/data/test1520 +++ b/tests/data/test1520 @@ -17,7 +17,7 @@ lib%TESTNUMBER -SMTP with CRLF-dot-CRLF in data +SMTP with CRLF-dot-CRLF in upload payload From: different diff --git a/tests/data/test153 b/tests/data/test153 index b2bd6e220b9b..e2b8ebf3fb84 100644 --- a/tests/data/test153 +++ b/tests/data/test153 @@ -56,7 +56,7 @@ Content-Length: 26 This is not the real page -# The second request to the 1002 section will bounce this one back instead +# The second request to the 1002 section bounces this one back instead # thanks to the swsbounce keyword up there HTTP/1.1 200 OK diff --git a/tests/data/test1538 b/tests/data/test1538 index e989fdd17f67..51580576ee2c 100644 --- a/tests/data/test1538 +++ b/tests/data/test1538 @@ -94,7 +94,7 @@ e62: Unknown error e63: Maximum file size exceeded e64: Requested SSL level failed e65: Send failed since rewinding of the data stream failed -e66: Failed to initialise SSL crypto engine +e66: Failed to initialize SSL crypto engine e67: Login denied e68: TFTP: File Not Found e69: TFTP: Access Violation diff --git a/tests/data/test1541 b/tests/data/test1541 index ecba40ff9319..2f030cc904b1 100644 --- a/tests/data/test1541 +++ b/tests/data/test1541 @@ -56,7 +56,7 @@ http lib%TESTNUMBER -chunked with trailers and pausing the receive +CURLINFO timer check http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test1553 b/tests/data/test1553 index fbf5390b156b..84f383085225 100644 --- a/tests/data/test1553 +++ b/tests/data/test1553 @@ -34,7 +34,7 @@ Mime imap -IMAP cleanup before a connection was created +IMAP cleanup with non-existing hostname # tool is what to use instead of 'curl' diff --git a/tests/data/test1555 b/tests/data/test1555 index 2cbbd9b0c6d3..1212531686f1 100644 --- a/tests/data/test1555 +++ b/tests/data/test1555 @@ -30,7 +30,7 @@ lib%TESTNUMBER verify api is protected against calls from callbacks -# this server/host will not be used for real +# this server/host is not used for real http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test1560 b/tests/data/test1560 index e0d792800b2e..bd72d35d29f3 100644 --- a/tests/data/test1560 +++ b/tests/data/test1560 @@ -37,7 +37,7 @@ lib%TESTNUMBER success -Allocations: 3100 +Allocations: 3350 diff --git a/tests/data/test157 b/tests/data/test157 index 422d88f69aee..fe9c9bdd920b 100644 --- a/tests/data/test157 +++ b/tests/data/test157 @@ -16,7 +16,7 @@ Server: Apache/1.3.27 (Darwin) PHP/4.1.2 Content-Type: text/html; charset=iso-8859-1 Connection: close -GET received and served just fine. Thank you very much +GET received and served fine. Thank you very much diff --git a/tests/data/test1588 b/tests/data/test1588 index 753e98cd6b6a..30ec8ca91282 100644 --- a/tests/data/test1588 +++ b/tests/data/test1588 @@ -79,25 +79,25 @@ http://test.remote.example.com/path/%TESTNUMBER %HOSTIP %HTTPPORT silly:person c # Verify data after the test has been "shot" -GET http://test.remote.example.com/path/1588 HTTP/1.1 +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 Host: test.remote.example.com Accept: */* Proxy-Connection: Keep-Alive -GET http://test.remote.example.com/path/1588 HTTP/1.1 +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 Host: test.remote.example.com -Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/1588", response="d0b2f000c7e3fca24452b5810713404a" +Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="d0b2f000c7e3fca24452b5810713404a" Accept: */* Proxy-Connection: Keep-Alive -GET http://test.remote.example.com/path/1588 HTTP/1.1 +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 Host: test.remote.example.com Accept: */* Proxy-Connection: Keep-Alive -GET http://test.remote.example.com/path/1588 HTTP/1.1 +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 Host: test.remote.example.com -Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/1588", response="d0b2f000c7e3fca24452b5810713404a" +Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="d0b2f000c7e3fca24452b5810713404a" Accept: */* Proxy-Connection: Keep-Alive diff --git a/tests/data/test1589 b/tests/data/test1589 new file mode 100644 index 000000000000..527edb54cab4 --- /dev/null +++ b/tests/data/test1589 @@ -0,0 +1,108 @@ + + + + +HTTP +HTTP proxy +HTTP proxy Digest auth + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +And you should ignore this data. + + +# then this is returned when we get proxy-auth + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! + + + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! + + + +# Client-side + + +http +http-proxy + +# tool is what to use instead of 'curl' + +lib%TESTNUMBER + + +!SSPI +crypto +proxy +digest + + +HTTP proxy auth Digest, then change proxy port and do it again + + +http://test.remote.example.com/path/%TESTNUMBER %HOSTIP %HTTPPORT %PROXYPORT silly:person + + + +# Verify data after the test has been "shot" + + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="9a547f8fa81cff330c68095603f3819e" +Accept: */* +Proxy-Connection: Keep-Alive + + + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Proxy-Authorization: Digest username="silly", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="9a547f8fa81cff330c68095603f3819e" +Accept: */* +Proxy-Connection: Keep-Alive + + + + + diff --git a/tests/data/test161 b/tests/data/test161 index 16fdaf41862e..23db19005e2e 100644 --- a/tests/data/test161 +++ b/tests/data/test161 @@ -23,7 +23,7 @@ PASV ftp -FTP RETR PASV +FTP RETR partial file ftp://%HOSTIP:%FTPPORT/%TESTNUMBER diff --git a/tests/data/test162 b/tests/data/test162 index cef8fc2f08e7..d03df40bb497 100644 --- a/tests/data/test162 +++ b/tests/data/test162 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy HTTP proxy NTLM auth -FAILURE diff --git a/tests/data/test1628 b/tests/data/test1628 new file mode 100644 index 000000000000..f4f1da5d8df4 --- /dev/null +++ b/tests/data/test1628 @@ -0,0 +1,53 @@ + + + + +HTTP +HTTP PUT + + +# Server-side + + +HTTP/1.0 200 OK swsclose +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake + +blablabla + + + + +# Client-side + + +proxy + + +http + + +HTTP PUT from file with weird letters over an HTTP proxy + + +-x http://%HOSTIP:%HTTPPORT http://ssss/ -T %LOGDIR/%TESTNUMBERte[]st.txt + + +a few bytes + + + +# Verify data after the test has been "shot" + + +PUT http://ssss/%TESTNUMBERte%5B%5Dst.txt HTTP/1.1 +Host: ssss +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive +Content-Length: 12 + +a few bytes + + + diff --git a/tests/data/test1629 b/tests/data/test1629 new file mode 100644 index 000000000000..f5e1dcbee4cb --- /dev/null +++ b/tests/data/test1629 @@ -0,0 +1,54 @@ + + + + +HTTP +HTTP GET +--resolve + + + +# Server-side + + +HTTP/1.1 200 OK +Content-Length: 6 +Set-Cookie: something=1; Domain=co.uk.; Path=/ + +-foo- + + + +# Client-side + + +PSL +cookies + + +http + + +cookies with trailing dot after PSL domain + + +http://foo.co.uk.:%HTTPPORT/ http://bar.co.uk.:%HTTPPORT/ -b "" --resolve foo.co.uk.:%HTTPPORT:%HOSTIP --resolve bar.co.uk.:%HTTPPORT:%HOSTIP + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: foo.co.uk.:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET / HTTP/1.1 +Host: bar.co.uk.:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test1631 b/tests/data/test1631 index 2015fc328ac3..f0018516d598 100644 --- a/tests/data/test1631 +++ b/tests/data/test1631 @@ -56,8 +56,8 @@ proxy # Verify data after the test has been "shot" -# The second CONNECT will be made to the dynamic port number the FTP server -# opens for us, so we cannot compare with a known pre-existing number! +# The second CONNECT is made to the dynamic port number the FTP server +# opens for us, so we cannot compare with a known pre-existing number. s/((https.proxy):(\d+))/$2:12345/ diff --git a/tests/data/test1632 b/tests/data/test1632 index 76c8bffcba0f..eda70d0d98b5 100644 --- a/tests/data/test1632 +++ b/tests/data/test1632 @@ -65,8 +65,8 @@ proxy # Verify data after the test has been "shot" -# The second and third CONNECT will be made to the dynamic port number the FTP -# server opens for us, so we cannot compare with known pre-existing numbers! +# The second and third CONNECTs are made to the dynamic port number the FTP +# server opens for us, so we cannot compare with known pre-existing numbers. s/((https.proxy):(\d+))/$2:12345/ diff --git a/tests/data/test1645 b/tests/data/test1645 new file mode 100644 index 000000000000..70666ef34c48 --- /dev/null +++ b/tests/data/test1645 @@ -0,0 +1,74 @@ + + + + +HTTP +HTTP GET +cookies +cookiejar +--resolve + + +# Server-side + + + +HTTP/1.1 200 OK +Content-Length: 4 +Content-Type: text/html +Funny-head: yesyes +Set-Cookie: name=value; domain=test.curl; path=/we/want + +boo + + +HTTP/1.1 200 OK +Content-Length: 4 +Content-Type: text/html +Funny-head: yesyes +Set-Cookie: name=value; domain=test.curl; path=/WE/WANT + +boo + + + +# Client-side + + +http + + +cookies for paths using different case + + +http://test.curl:%HTTPPORT/we/want/ http://test.curl:%HTTPPORT/WE/WANT/%TESTNUMBER0002 -c %LOGDIR/jar%TESTNUMBER.txt --resolve test.curl:%HTTPPORT:%HOSTIP + + +cookies + + + +# Verify data after the test has been "shot" + + +GET /we/want/ HTTP/1.1 +Host: test.curl:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /WE/WANT/%TESTNUMBER0002 HTTP/1.1 +Host: test.curl:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +# Netscape HTTP Cookie File +# https://curl.se/docs/http-cookies.html +# This file was generated by libcurl! Edit at your own risk. + +.test.curl TRUE /WE/WANT FALSE 0 name value +.test.curl TRUE /we/want FALSE 0 name value + + + diff --git a/tests/data/test1646 b/tests/data/test1646 new file mode 100644 index 000000000000..6a292ebd2664 --- /dev/null +++ b/tests/data/test1646 @@ -0,0 +1,46 @@ + + + + +netrc +--resolve + + + + + +HTTP/1.1 200 OK +Content-Length: 6 + +12345 + + + +# Client-side + + +http + + +netrc parsing without user match but user in URL + + +--netrc --netrc-file %LOGDIR/netrc%TESTNUMBER http://alice@example.com:%HTTPPORT/%TESTNUMBER --resolve example.com:%HTTPPORT:%HOSTIP + + +machine example.com login bob password sekret + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: example.com:%HTTPPORT +Authorization: Basic %b64[alice:]b64% +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test1647 b/tests/data/test1647 new file mode 100644 index 000000000000..a87487fa9f0c --- /dev/null +++ b/tests/data/test1647 @@ -0,0 +1,103 @@ + + + + +HTTP +HTTP GET +HTTP proxy +HTTP proxy Digest auth +multi + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +And you should ignore this data. + + +# then this is returned when we get proxy-auth + +HTTP/1.1 200 OK +Content-Length: 21 +Server: no + +Nice proxy auth sir! + + + +HTTP/1.1 401 OK +Content-Length: 21 +Server: no + +Denied access. Leave + + + + +# Client-side + + +http +https-proxy +https + +# tool is what to use instead of 'curl' + +lib%TESTNUMBER + + +!SSPI +crypto +proxy +digest +Debug + + +http_proxy=%HOSTIP:%HTTPPORT +https_proxy=https://%HOSTIP:%HTTPSPROXYPORT +CURL_ENTROPY=99376 + + +HTTP proxy auth Digest, then change proxy with env var and do it again + + +http://test.remote.example.com/path/%TESTNUMBER https://another.example.com:%HTTPSPORT/ daniel:monkey123 another:bump456 + + + +# Verify data after the test has been "shot" + + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://test.remote.example.com/path/%TESTNUMBER HTTP/1.1 +Host: test.remote.example.com +Proxy-Authorization: Digest username="daniel", realm="weirdorealm", nonce="12345", uri="/path/%TESTNUMBER", response="7a1672891aff03248887b1a6674b8096" +Accept: */* +Proxy-Connection: Keep-Alive + + + + +CONNECT another.example.com:%HTTPSPORT HTTP/1.1 +Host: another.example.com:%HTTPSPORT +Proxy-Connection: Keep-Alive + + + +# CONNECT fails + +7 + + + diff --git a/tests/data/test1648 b/tests/data/test1648 new file mode 100644 index 000000000000..623f3c9a81ae --- /dev/null +++ b/tests/data/test1648 @@ -0,0 +1,63 @@ + + + + +HTTP +HTTP GET +HTTP proxy +HTTP proxy auth + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 407 Authorization Required to proxy me my dear +Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345" +Content-Length: 33 + +And you should ignore this data. + + + + +# Client-side + + +http + +# tool is what to use instead of 'curl' + +lib%TESTNUMBER + + +proxy + + +HTTP proxy with auth, change proxy, clear auth + + +%HOSTIP %HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET http://example.com/ HTTP/1.1 +Host: example.com +Proxy-Authorization: Basic %b64[victim:secret]b64% +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://example.com/ HTTP/1.1 +Host: example.com +Accept: */* +Proxy-Connection: Keep-Alive + + + + + diff --git a/tests/data/test1649 b/tests/data/test1649 new file mode 100644 index 000000000000..d2fd7799bd87 --- /dev/null +++ b/tests/data/test1649 @@ -0,0 +1,55 @@ + + + + +HTTP +Referer + + + +# Server-side + + +# this is returned first since we get no proxy-auth + +HTTP/1.1 200 OK +Content-Length: 6 + +hello + + + + +# Client-side + + +http + + + +lib%TESTNUMBER + + +Set referer first then NULL it + + +http://%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* +Referer: https://secret.example.com/ + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + + diff --git a/tests/data/test1676 b/tests/data/test1676 new file mode 100644 index 000000000000..7f4907fea330 --- /dev/null +++ b/tests/data/test1676 @@ -0,0 +1,21 @@ + + + + +unittest +x509 +DH + + + +# Client-side + + +unittest + + +x509 DH public key parameter extraction + + + + diff --git a/tests/data/test1677 b/tests/data/test1677 new file mode 100644 index 000000000000..dbf5ca17a744 --- /dev/null +++ b/tests/data/test1677 @@ -0,0 +1,77 @@ + + + + +HTTP +HTTP POST +chunked Transfer-Encoding +Content-Length + + + +# Regression test for bug where curl stops reading chunked response +# when both Content-Length: 0 and Transfer-Encoding: chunked headers +# are present. Per RFC 7230 Section 3.3.3, Transfer-Encoding should +# take precedence and Content-Length should be ignored. +# The writedelay simulates the timing issue where chunks arrive in +# separate packets. + +# Server-side + + +writedelay: 500 + + +HTTP/1.1 200 OK +content-type: text/json +connection: keep-alive +content-length: 0 +transfer-encoding: chunked + +1a +random data in first chunk +1d + another data in second chunk +15 + third and last chunk +0 + + + + +# Client-side + + +http + + +HTTP POST response with both Transfer-Encoding chunked and Content-Length 0 + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -d "testdata" + + + +# Verify data after the test has been "shot" + + +POST /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 8 +Content-Type: application/x-www-form-urlencoded + +testdata + + +HTTP/1.1 200 OK +content-type: text/json +connection: keep-alive +content-length: 0 +transfer-encoding: chunked + +random data in first chunk another data in second chunk third and last chunk + + + diff --git a/tests/data/test1686 b/tests/data/test1686 new file mode 100644 index 000000000000..2d419ad60834 --- /dev/null +++ b/tests/data/test1686 @@ -0,0 +1,84 @@ + + + + +HTTP +Digest + + + + + +HTTP/1.1 401 Authorization Required +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +WWW-Authenticate: Digest realm="my-backyard", nonce="314156295" +Content-Length: 26 + +This is not the real page + + +# This is supposed to be returned when the server gets a +# Authorization: Digest line passed-in from the client + +HTTP/1.1 200 OK +Server: Apache/1.3.27 (Darwin) PHP/4.1.2 +Content-Type: text/html; charset=iso-8859-1 +Content-Length: 23 + +This IS the real page! + + + + + + +!SSPI +crypto +digest + + +http + + +HTTP Digest to different origins and switching credentials + + +lib%TESTNUMBER + + +%HOSTIP %HTTPPORT + + + + + +GET /api HTTP/1.1 +Host: first.test:%HTTPPORT +Accept: */* + +GET /api HTTP/1.1 +Host: first.test:%HTTPPORT +Authorization: Digest username="alice", realm="my-backyard", nonce="314156295", uri="/api", response="4ecc00e567c37a9d537727890c2e5b32" +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Authorization: Digest username="alice", realm="my-backyard", nonce="314156295", uri="/hook", response="d3a7738fb6a23f5543fb8dacc0f0f253" +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Accept: */* + +GET /hook HTTP/1.1 +Host: second.test:%HTTPPORT +Authorization: Digest username="bob", realm="my-backyard", nonce="314156295", uri="/hook", response="777e68eddb77294d9cbd6134973cbbab" +Accept: */* + + + + diff --git a/tests/data/test1721 b/tests/data/test1721 new file mode 100644 index 000000000000..1b0e7a88f78d --- /dev/null +++ b/tests/data/test1721 @@ -0,0 +1,45 @@ + + + + +HTTP +HTTP GET + + + + + +HTTP/1.1 200 OK +Content-Length: 6 +Content-Type: text/html + +-foo- + + + + + +http + + +Keep question mark for empty query + + +"http://%HOSTIP:%HTTPPORT/hello?" -w '%output{%LOGDIR/out%TESTNUMBER}%{url_effective}' + + + +# Verify data after the test has been "shot" + + +http://%HOSTIP:%HTTPPORT/hello? + + +GET /hello? HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test1722 b/tests/data/test1722 new file mode 100644 index 000000000000..e51f26f4b432 --- /dev/null +++ b/tests/data/test1722 @@ -0,0 +1,61 @@ + + + + +HTTP +HTTP GET +chunked Transfer-Encoding + + +# Server-side + + +HTTP/1.1 200 funky chunky! +Server: fakeit/0.9 fakeitbad/1.0 +Transfer-Encoding: chunked, another +Connection: mooo + +40%CR +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%CR +30%CR +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb%CR +21;heresatest=moooo%CR +cccccccccccccccccccccccccccccccc +%CR +0%CR +%CR + + +HTTP/1.1 200 funky chunky! +Server: fakeit/0.9 fakeitbad/1.0 + + + +# Client-side + + +http + + +HTTP with chunked Transfer-Encoding not listed last + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +61 + + + + diff --git a/tests/data/test1723 b/tests/data/test1723 new file mode 100644 index 000000000000..ad93c3016883 --- /dev/null +++ b/tests/data/test1723 @@ -0,0 +1,61 @@ + + + + +HTTP +HTTP GET +chunked Transfer-Encoding + + +# Server-side + + +HTTP/1.1 200 funky chunky! +Server: fakeit/0.9 fakeitbad/1.0 +Transfer-Encoding: chunked, chunked, another +Connection: mooo + +40%CR +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%CR +30%CR +bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb%CR +21;heresatest=moooo%CR +cccccccccccccccccccccccccccccccc +%CR +0%CR +%CR + + +HTTP/1.1 200 funky chunky! +Server: fakeit/0.9 fakeitbad/1.0 + + + +# Client-side + + +http + + +HTTP with two chunked Transfer-Encoding not listed last + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +61 + + + + diff --git a/tests/data/test1724 b/tests/data/test1724 new file mode 100644 index 000000000000..3cd328e39c39 --- /dev/null +++ b/tests/data/test1724 @@ -0,0 +1,53 @@ + + + + +IPFS + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 21 +Connection: close +Content-Type: text/plain +Funny-head: yesyes + +Hello curl from IPFS + + + +# Client-side + + +ipfs + + +http + + +IPFS with --proto-default HTTP + + +--ipfs-gateway http://%HOSTIP:%HTTPPORT ipfs://bafybeidecnvkrygux6uoukouzps5ofkeevoqland7kopseiod6pzqvjg7u --proto-default http + + + +# Verify data after the test has been "shot" + + +GET /ipfs/bafybeidecnvkrygux6uoukouzps5ofkeevoqland7kopseiod6pzqvjg7u HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test1725 b/tests/data/test1725 new file mode 100644 index 000000000000..2a882c791d03 --- /dev/null +++ b/tests/data/test1725 @@ -0,0 +1,29 @@ + + + + +SCP +server key check + + + +# Client-side + + +scp + + +SCP incorrect host key with --proto-default SCP + + +--hostpubmd5 00000000000000000000000000000000 --key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: %HOSTIP:%SSHPORT%SCP_PWD/%LOGDIR/irrelevant-file --insecure --proto-default SCP + + + +# Verify data after the test has been "shot" + + +60 + + + diff --git a/tests/data/test19 b/tests/data/test19 index 0c1b08b4c0b1..814163859c69 100644 --- a/tests/data/test19 +++ b/tests/data/test19 @@ -4,7 +4,6 @@ HTTP connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test190 b/tests/data/test190 index 4b4da41b62e1..f59f871d6a36 100644 --- a/tests/data/test190 +++ b/tests/data/test190 @@ -4,7 +4,6 @@ FTP timeout -FAILURE DELAY diff --git a/tests/data/test1921 b/tests/data/test1921 new file mode 100644 index 000000000000..15a3fe1ae7b9 --- /dev/null +++ b/tests/data/test1921 @@ -0,0 +1,30 @@ + + + + +urlapi + + + +# Client-side + + + +Set a URL without scheme, then redirect with default scheme + + +lib%TESTNUMBER + + + +- + + + + + +URL: https://example.com/newpath + + + + diff --git a/tests/data/test1922 b/tests/data/test1922 new file mode 100644 index 000000000000..9c35ccff6f18 --- /dev/null +++ b/tests/data/test1922 @@ -0,0 +1,91 @@ + + + + +HTTP +HTTP proxy +HSTS +curl_easy_duphandle + + + + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Type: text/plain +Content-Length: 5 +Strict-Transport-Security: max-age=31536000 + +Hello + + + + +HTTP/1.1 403 Forbidden +Content-Length: 0 +Connection: close + + + + + + +HSTS +https +Debug +proxy + + +http +http-proxy + + +CURL_HSTS_HTTP=yes + + +curl_easy_duphandle copies HSTS cache, auto upgrading HTTP to HTTPS. + + +lib%TESTNUMBER + + +- %HOSTIP %HTTPPORT %PROXYPORT + + + + +# First request: original handle GETs from the http server; the response +# carries Strict-Transport-Security, populating the live HSTS cache that +# the dup inherits. + +GET /%TESTNUMBER HTTP/1.1 +Host: hsts.example.com:%HTTPPORT +Accept: */* + + +# Second request: dup handle upgraded HTTP to HTTPS by copied HSTS cache, +# proxy receives CONNECT to port 443 proving the upgrade happened + +CONNECT hsts.example.com:443 HTTP/1.1 +Host: hsts.example.com:443 +Proxy-Connection: Keep-Alive + + + +First request: HSTS cache populated +Dup effective URL: https://hsts.example.com/%TESTNUMBER + +# CURLE_COULDNT_CONNECT (7) is intentional: The proxy rejects the CONNECT +# to port 443, collapsing the tunnel. All that is being validated is the +# CONNECT to port 443 itself. + +7 + + + diff --git a/tests/data/test1933 b/tests/data/test1933 index e1ef8b95ba13..cda12d19a121 100644 --- a/tests/data/test1933 +++ b/tests/data/test1933 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1934 b/tests/data/test1934 index 2128b299c2eb..9c52a0d0bddd 100644 --- a/tests/data/test1934 +++ b/tests/data/test1934 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1935 b/tests/data/test1935 index 158620a3bd29..f92c7e8ea2d3 100644 --- a/tests/data/test1935 +++ b/tests/data/test1935 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1936 b/tests/data/test1936 index 330fccc75650..e43eb0be6916 100644 --- a/tests/data/test1936 +++ b/tests/data/test1936 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1937 b/tests/data/test1937 index 19049410be98..2043053e59db 100644 --- a/tests/data/test1937 +++ b/tests/data/test1937 @@ -5,6 +5,7 @@ HTTP HTTP POST CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1938 b/tests/data/test1938 index 91e27f876833..a2ea20fc9662 100644 --- a/tests/data/test1938 +++ b/tests/data/test1938 @@ -5,6 +5,7 @@ HTTP HTTP POST CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1955 b/tests/data/test1955 index 7b84f746bd04..82349c25395b 100644 --- a/tests/data/test1955 +++ b/tests/data/test1955 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1956 b/tests/data/test1956 index 3e8a9f9ce449..f3ad8b859a14 100644 --- a/tests/data/test1956 +++ b/tests/data/test1956 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1957 b/tests/data/test1957 index 181eabb9f5af..77b48b238897 100644 --- a/tests/data/test1957 +++ b/tests/data/test1957 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1959 b/tests/data/test1959 index 7f0357da8f4b..27a778c305ac 100644 --- a/tests/data/test1959 +++ b/tests/data/test1959 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1966 b/tests/data/test1966 index 49270d03a98e..914a1166e934 100644 --- a/tests/data/test1966 +++ b/tests/data/test1966 @@ -5,6 +5,7 @@ HTTP HTTP GET HTTP Digest auth +--resolve # Server-side diff --git a/tests/data/test1967 b/tests/data/test1967 new file mode 100644 index 000000000000..c012a70ba63c --- /dev/null +++ b/tests/data/test1967 @@ -0,0 +1,30 @@ + + + + +HTTP +urlapi + + + + + + +curl_url_set() a URL without guessing a scheme + + +lib%TESTNUMBER + + + +http://%HOSTIP:%NOLISTENPORT/not-there/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +URL http://a.b/x + + + diff --git a/tests/data/test1970 b/tests/data/test1970 index e697cabfeec6..8dae28c75339 100644 --- a/tests/data/test1970 +++ b/tests/data/test1970 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1971 b/tests/data/test1971 index bc44cd3eaa82..c303018c72df 100644 --- a/tests/data/test1971 +++ b/tests/data/test1971 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1972 b/tests/data/test1972 index 7de801da7e97..d128dccf452c 100644 --- a/tests/data/test1972 +++ b/tests/data/test1972 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1973 b/tests/data/test1973 index 896631f1514d..c2527df686f4 100644 --- a/tests/data/test1973 +++ b/tests/data/test1973 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1974 b/tests/data/test1974 index 6a99684d8855..20a10a87056d 100644 --- a/tests/data/test1974 +++ b/tests/data/test1974 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1975 b/tests/data/test1975 index a4d1a7f0f750..e5d6272eda0b 100644 --- a/tests/data/test1975 +++ b/tests/data/test1975 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1976 b/tests/data/test1976 index 5a0ff39dc086..3a09cc2ca31e 100644 --- a/tests/data/test1976 +++ b/tests/data/test1976 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1978 b/tests/data/test1978 index 3ad4c670e2be..b2a4285b96ae 100644 --- a/tests/data/test1978 +++ b/tests/data/test1978 @@ -4,6 +4,7 @@ HTTP CURLOPT_AWS_SIGV4 +aws-sigv4 diff --git a/tests/data/test1981 b/tests/data/test1981 index 1e2519a72a9b..e1fb24bf359c 100644 --- a/tests/data/test1981 +++ b/tests/data/test1981 @@ -38,9 +38,10 @@ Debug CURL_TIME=1754037103 +LC_TIME=C -http://%HOSTIP:%HTTPPORT/%TESTNUMBER --write-out='Time: %time{%d/%b/%Y %H:%M:%S.%f %z %Z}\n' -s -o %LOGDIR/dump +http://%HOSTIP:%HTTPPORT/%TESTNUMBER --write-out='Time: %time{%d/%b/%Y %H:%M:%S.%f %z %Z%%s %s}\n' -s -o %LOGDIR/dump @@ -54,7 +55,7 @@ Accept: */* -Time: 01/Aug/2025 08:31:43.037103 +0000 UTC +Time: 01/Aug/2025 08:31:43.037103 +0000 UTC%s 1754037103 diff --git a/tests/data/test20 b/tests/data/test20 index a851ba30d0a3..5dfe22dcd716 100644 --- a/tests/data/test20 +++ b/tests/data/test20 @@ -3,7 +3,6 @@ HTTP -FAILURE non-existing host diff --git a/tests/data/test2008 b/tests/data/test2008 index a24a01935639..9d1b14183391 100644 --- a/tests/data/test2008 +++ b/tests/data/test2008 @@ -5,6 +5,7 @@ HTTP -G --proto-default +--resolve diff --git a/tests/data/test201 b/tests/data/test201 index 7a1699d3e47f..ac1593c8e2c5 100644 --- a/tests/data/test201 +++ b/tests/data/test201 @@ -3,7 +3,6 @@ FILE -FAILURE diff --git a/tests/data/test2014 b/tests/data/test2014 new file mode 100644 index 000000000000..a7fb078db635 --- /dev/null +++ b/tests/data/test2014 @@ -0,0 +1,97 @@ + + + + +HTTP +HTTP PUT + + + +# Server-side + + +HTTP/1.1 200 OK swsbounce +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Accept-Ranges: bytes +Content-Length: 6 +Content-Type: text/html + +-foo- + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 20 +Content-Type: text/html + +the second response + + + +# Client-side + + +http + + +upload with glob, output name based on upload glob + + +-T '%LOGDIR/upload{%LThej%GT1,2}' http://%HOSTIP:%HTTPPORT/%TESTNUMBER --silent '--output=%LOGDIR/out-#%LThej%GT' + + + +first! + + + +second + + + + +# Verify data after the test has been "shot" + + +PUT /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 7 + +first! +PUT /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Content-Length: 7 + +second + + +%EMPTY + + + +HTTP/1.1 200 OK swsbounce +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Accept-Ranges: bytes +Content-Length: 6 +Content-Type: text/html + +-foo- + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 20 +Content-Type: text/html + +the second response + + + + diff --git a/tests/data/test2015 b/tests/data/test2015 new file mode 100644 index 000000000000..d0479c5b7bd6 --- /dev/null +++ b/tests/data/test2015 @@ -0,0 +1,92 @@ + + + + +HTTP +HTTP proxy +followlocation +cookies + + +# Server-side + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +contents + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +HTTP/1.1 302 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Location: http://goto.second.host.now/%TESTNUMBER0002 +Content-Length: 8 +Connection: close + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake swsclose +Content-Type: text/html +Funny-head: yesyes +Content-Length: 9 + +contents + + + +# Client-side + + +http + + +HTTP with cookie with with -b and redirect to new host + + +http://first.host.it.is/we/want/that/page/%TESTNUMBER -x %HOSTIP:%HTTPPORT -b "test=yes" --location + + +cookies +proxy + + + +# Verify data after the test has been "shot" + + +GET http://first.host.it.is/we/want/that/page/%TESTNUMBER HTTP/1.1 +Host: first.host.it.is +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive +Cookie: test=yes + +GET http://goto.second.host.now/%TESTNUMBER0002 HTTP/1.1 +Host: goto.second.host.now +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + diff --git a/tests/data/test2027 b/tests/data/test2027 index 2a5fa6e3db15..dfcefa302a16 100644 --- a/tests/data/test2027 +++ b/tests/data/test2027 @@ -16,7 +16,7 @@ Explanation for the duplicate 400 requests: libcurl does not detect that a given Digest password is wrong already on the first 401 response (as the data400 gives). libcurl will instead consider the -new response just as a duplicate and it sends another and detects the auth +new response as a duplicate and it sends another and detects the auth problem on the second 401 response! --> diff --git a/tests/data/test2030 b/tests/data/test2030 index e0d7ba551374..5027fd45da4b 100644 --- a/tests/data/test2030 +++ b/tests/data/test2030 @@ -21,7 +21,7 @@ Explanation for the duplicate 400 requests: libcurl does not detect that a given Digest password is wrong already on the first 401 response (as the data400 gives). libcurl will instead consider the -new response just as a duplicate and it sends another and detects the auth +new response as a duplicate and it sends another and detects the auth problem on the second 401 response! --> diff --git a/tests/data/test2036 b/tests/data/test2036 new file mode 100644 index 000000000000..b017a71abc8e --- /dev/null +++ b/tests/data/test2036 @@ -0,0 +1,26 @@ + + + + +--proto-default + + + +# Client-side + + +Attempt to set a default protocol with :// suffix + + +--proto-default https:// + + + +# Verify data after the test has been "shot" + +# CURLE_UNSUPPORTED_PROTOCOL is error code 1 + +1 + + + diff --git a/tests/data/test2045 b/tests/data/test2045 index 597deed7f66c..dd814abb6cb9 100644 --- a/tests/data/test2045 +++ b/tests/data/test2045 @@ -11,13 +11,13 @@ FTP # The purpose of this test is to make sure the --proto-default option works # properly. We specify a default protocol of FTP and if the option works properly -# curl will use the FTP protocol. If the option is broken however curl will use +# curl uses the FTP protocol. If the option is broken however curl uses # the HTTP protocol. # In the broken scenario curl would use HTTP to talk to our FTP server. We handle # that by replying with something that both protocols can understand. Our FTP # server allows a custom welcome message, so we use that feature to make an HTTP # reply that contains an FTP reply (think polyglot). In the case of FTP we expect -# curl will return CURLE_WEIRD_SERVER_REPLY so we test for that return code. +# curl to return CURLE_WEIRD_SERVER_REPLY so we test for that return code. REPLY welcome HTTP/1.1 200 OK\r\nContent-Length: 21\r\n\r\n500 Weird FTP Reply diff --git a/tests/data/test205 b/tests/data/test205 index 278562532357..3324d7d64f4f 100644 --- a/tests/data/test205 +++ b/tests/data/test205 @@ -3,7 +3,6 @@ FILE -FAILURE diff --git a/tests/data/test2050 b/tests/data/test2050 index ef20c69b828e..3c2a9b40f5f7 100644 --- a/tests/data/test2050 +++ b/tests/data/test2050 @@ -50,7 +50,8 @@ http-proxy -http://www.example.com.%TESTNUMBER/%TESTNUMBER --connect-to ::connect.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT +http://www.example.com.%TESTNUMBER/%TESTNUMBER --connect-to ::connect.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT --next +http://www.example.com.%TESTNUMBER:%HTTPPORT/%TESTNUMBER --connect-to ::www.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT proxy @@ -59,12 +60,18 @@ proxy # Verify data after the test has been "shot" - + CONNECT connect.example.com.%TESTNUMBER:%HTTPPORT HTTP/1.1 Host: connect.example.com.%TESTNUMBER:%HTTPPORT User-Agent: curl/%VERSION Proxy-Connection: Keep-Alive +GET http://www.example.com.%TESTNUMBER:%HTTPPORT/%TESTNUMBER HTTP/1.1 +Host: www.example.com.%TESTNUMBER:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + GET /%TESTNUMBER HTTP/1.1 diff --git a/tests/data/test2055 b/tests/data/test2055 index 608facb86e45..6a7a8d34ac61 100644 --- a/tests/data/test2055 +++ b/tests/data/test2055 @@ -54,18 +54,25 @@ socks5 proxy -http://www.example.com.%TESTNUMBER/%TESTNUMBER --connect-to ::connect.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT --preproxy socks5://%HOSTIP:%SOCKSPORT +http://www.example.com.%TESTNUMBER/%TESTNUMBER --connect-to ::connect.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT --preproxy socks5://%HOSTIP:%SOCKSPORT --next +http://www.example.com.%TESTNUMBER:%HTTPPORT/%TESTNUMBER --connect-to ::www.example.com.%TESTNUMBER:%HTTPPORT -x %HOSTIP:%PROXYPORT --preproxy socks5://%HOSTIP:%SOCKSPORT # Verify data after the test has been "shot" - + CONNECT connect.example.com.%TESTNUMBER:%HTTPPORT HTTP/1.1 Host: connect.example.com.%TESTNUMBER:%HTTPPORT User-Agent: curl/%VERSION Proxy-Connection: Keep-Alive +GET http://www.example.com.%TESTNUMBER:%HTTPPORT/%TESTNUMBER HTTP/1.1 +Host: www.example.com.%TESTNUMBER:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + GET /%TESTNUMBER HTTP/1.1 diff --git a/tests/data/test207 b/tests/data/test207 index f1c0cf81fb63..8ac782f067bd 100644 --- a/tests/data/test207 +++ b/tests/data/test207 @@ -5,7 +5,6 @@ HTTP HTTP GET CURLE_PARTIAL_FILE -FAILURE chunked Transfer-Encoding diff --git a/tests/data/test2074 b/tests/data/test2074 index c1571231a2df..0967031906e0 100644 --- a/tests/data/test2074 +++ b/tests/data/test2074 @@ -32,7 +32,7 @@ Funny-head: yesyes http -HTTP GET +HTTP with oauth2-bearer http://%HOSTIP:%HTTPPORT/%TESTNUMBER --oauth2-bearer mF_9.B5f-4.1JqM diff --git a/tests/data/test2092 b/tests/data/test2092 new file mode 100644 index 000000000000..6cbf8563fb7a --- /dev/null +++ b/tests/data/test2092 @@ -0,0 +1,28 @@ + + + + +globbing + + + +# Client-side + + +http + + +glob range that ends with 9223372036854775807 + + +"%HOSTIP:%HTTPPORT/[0-1][9223372036854775806-9223372036854775807]/%TESTNUMBER" + + + +# Verify data after the test has been "shot" + + +3 + + + diff --git a/tests/data/test21 b/tests/data/test21 index af47cf914e03..8f3da43b8162 100644 --- a/tests/data/test21 +++ b/tests/data/test21 @@ -2,7 +2,6 @@ -FAILURE multiple HTTP requests diff --git a/tests/data/test2105 b/tests/data/test2105 new file mode 100644 index 000000000000..8bd26caff12d --- /dev/null +++ b/tests/data/test2105 @@ -0,0 +1,49 @@ + + + + +HTTP +HTTP GET + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/%CRfake +Content-Length: 6 +Funny-head: yesyes + +-foo- + + + +# Client-side + + +http + + +HTTP with spurious CR in received header + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +8 + + + diff --git a/tests/data/test2106 b/tests/data/test2106 new file mode 100644 index 000000000000..40d2dc8b62b6 --- /dev/null +++ b/tests/data/test2106 @@ -0,0 +1,53 @@ + + + + +HTTP +HTTP GET +chunked Transfer-Encoding + + + +# Server-side + + +HTTP/1.1 200 OK%CR +Server: test%CR +Transfer-Encoding: chunked%CR +Trailer: chunky-trailer%CR +%CR +6%CR +-foo-%CR +0%CR +chunky-trailer: he%hex[%00]hex%llo%CR +%CR + + + +# Client-side + + +http + + +HTTP chunked response with a nul byte in the trailer + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + +8 + + + diff --git a/tests/data/test2107 b/tests/data/test2107 new file mode 100644 index 000000000000..8c4404410997 --- /dev/null +++ b/tests/data/test2107 @@ -0,0 +1,49 @@ + + + + +HTTP +HTTP CONNECT +HTTP proxy +proxytunnel + + + +# Server-side + + +HTTP/1.1 200 OK +Content-Length: 0 + + + +HTTP/1.1 200 OK%CR +X-Evil: he%hex[%00]hex%llo%CR +%CR + + + +# Client-side + + +http +http-proxy + + +proxy + + +HTTP CONNECT response with a nul byte in a header + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER -p -x http://%HOSTIP:%PROXYPORT + + + +# Verify data after the test has been "shot" + + +8 + + + diff --git a/tests/data/test2108 b/tests/data/test2108 new file mode 100644 index 000000000000..481d09357e73 --- /dev/null +++ b/tests/data/test2108 @@ -0,0 +1,41 @@ + + + + +FTP + + +# Server-side + + +REPLY PASS 230 logged\x00 in + + + +# Client-side + + +ftp + + +FTP rejects a nul byte in a server response line + + +ftp://%HOSTIP:%FTPPORT/%TESTNUMBER + + + + +# Verify data after the test has been "shot" + + +USER anonymous +PASS ftp@example.com + + +# 8 == CURLE_WEIRD_SERVER_REPLY + +8 + + + diff --git a/tests/data/test218 b/tests/data/test218 index 41bd83818ef4..14fc604830e5 100644 --- a/tests/data/test218 +++ b/tests/data/test218 @@ -31,7 +31,7 @@ HTTP PUT from a file but enforce chunked transfer-encoding -T %LOGDIR/file%TESTNUMBER -H "Transfer-Encoding: chunked" http://%HOSTIP:%HTTPPORT/%TESTNUMBER -just some tiny teeny contents +some tiny teeny test contents @@ -45,7 +45,7 @@ Accept: */*%CR Transfer-Encoding: chunked%CR %CR 1e%CR -just some tiny teeny contents +some tiny teeny test contents %CR 0%CR %CR diff --git a/tests/data/test2200 b/tests/data/test2200 index 95baca6bc098..bc1652eb2e98 100644 --- a/tests/data/test2200 +++ b/tests/data/test2200 @@ -28,7 +28,7 @@ mqtt mqtt -MQTT SUBSCRIBE with user and password +MQTT SUBSCRIBE with user and password, not authorized mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER -u fakeuser:fakepasswd diff --git a/tests/data/test2203 b/tests/data/test2203 index 9d8c1da6793e..9dcfedadaf7d 100644 --- a/tests/data/test2203 +++ b/tests/data/test2203 @@ -28,7 +28,7 @@ mqtt mqtt -MQTT with error in CONNACK +MQTT with "no user or password" CONNACK mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER diff --git a/tests/data/test2206 b/tests/data/test2206 new file mode 100644 index 000000000000..5021c190f7f7 --- /dev/null +++ b/tests/data/test2206 @@ -0,0 +1,59 @@ + + + + +MQTT +MQTT SUBSCRIBE + + + +# Server-side + + +hello + + +# Send a PINGRESP (0xD0) with remaining_length=2 in place of the +# expected CONNACK. MQTT 3.1.1 s. 3.13.1 requires PINGRESP to have +# remaining_length == 0. curl must reject this with +# CURLE_WEIRD_SERVER_REPLY rather than dispatching to the CONNACK +# handler. + +PINGRESP-as-CONNACK TRUE + + + +# Client-side + + +mqtt + + +mqtt + + +MQTT reject PINGRESP with nonzero remaining_length in place of CONNACK + + +mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + +# Strip out the random part of the client id from the CONNECT message +# before comparison + +s/^(.* 00044d5154540402003c000c6375726c).*/$1/ + + +client CONNECT 18 00044d5154540402003c000c6375726c +server PINGRESP-as-CONNACK 2 d0020000 + + +# 8 is CURLE_WEIRD_SERVER_REPLY + +8 + + + diff --git a/tests/data/test2207 b/tests/data/test2207 new file mode 100644 index 000000000000..61b423059e84 --- /dev/null +++ b/tests/data/test2207 @@ -0,0 +1,59 @@ + + + + +MQTT +MQTT SUBSCRIBE + + + +# Server-side + + +hello + + +# Send a DISCONNECT with remaining_length=2 after the PUBLISH. +# MQTT 3.1.1 s. 3.14.1 requires DISCONNECT to have remaining_length == 0. +# curl must reject this with CURLE_WEIRD_SERVER_REPLY. + +DISCONNECT-malformed TRUE + + + +# Client-side + + +mqtt + + +mqtt + + +MQTT reject DISCONNECT with nonzero remaining_length + + +mqtt://%HOSTIP:%MQTTPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +s/^(.* 00044d5154540402003c000c6375726c).*/$1/ + + +client CONNECT 18 00044d5154540402003c000c6375726c +server CONNACK 2 20020000 +client SUBSCRIBE 9 000100043232303700 +server SUBACK 3 9003000100 +server PUBLISH c 300c00043232303768656c6c6f0a +server DISCONNECT-malformed 2 e0020000 + + +# 8 is CURLE_WEIRD_SERVER_REPLY + +8 + + + diff --git a/tests/data/test2208 b/tests/data/test2208 new file mode 100644 index 000000000000..354d1a2a7e5d --- /dev/null +++ b/tests/data/test2208 @@ -0,0 +1,90 @@ + + + + +HTTP +HTTP proxy +--location +HTTP Basic auth + + + +# Server-side + + +HTTP/1.1 301 redirect +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 +Connection: close +Content-Type: text/html +Location: http://@firsthost.com:9999/a/path/%TESTNUMBER0002 + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 4 +Connection: close +Content-Type: text/html + +hey + + + +HTTP/1.1 301 redirect +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 0 +Connection: close +Content-Type: text/html +Location: http://@firsthost.com:9999/a/path/%TESTNUMBER0002 + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 4 +Connection: close +Content-Type: text/html + +hey + + + + +# Client-side + + +proxy + + +http + + +HTTP auth on redirect with empty URL userinfo + + +-x http://%HOSTIP:%HTTPPORT http://firsthost.com -L -u joe:secret + + + +# Verify data after the test has been "shot" + + +GET http://firsthost.com/ HTTP/1.1 +Host: firsthost.com +Authorization: Basic %b64[joe:secret]b64% +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + +GET http://firsthost.com:9999/a/path/%TESTNUMBER0002 HTTP/1.1 +Host: firsthost.com:9999 +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + diff --git a/tests/data/test221 b/tests/data/test221 index 5a7d082e7954..03f6d0505537 100644 --- a/tests/data/test221 +++ b/tests/data/test221 @@ -5,7 +5,6 @@ HTTP HTTP GET compressed -FAILURE # Server-side diff --git a/tests/data/test223 b/tests/data/test223 index 9912d3d47185..660c85da129c 100644 --- a/tests/data/test223 +++ b/tests/data/test223 @@ -5,7 +5,6 @@ HTTP HTTP GET compressed -FAILURE # Server-side diff --git a/tests/data/test225 b/tests/data/test225 index 1ce22088ee0d..403bd2fec2f1 100644 --- a/tests/data/test225 +++ b/tests/data/test225 @@ -3,7 +3,6 @@ FTP -FAILURE # Client-side diff --git a/tests/data/test226 b/tests/data/test226 index 1872981d644f..dbf6d1af38f0 100644 --- a/tests/data/test226 +++ b/tests/data/test226 @@ -3,7 +3,6 @@ FTP -FAILURE diff --git a/tests/data/test229 b/tests/data/test229 index b90214ff5107..bd911db9ee95 100644 --- a/tests/data/test229 +++ b/tests/data/test229 @@ -4,7 +4,6 @@ FTP ACCT -FAILURE # Server-side diff --git a/tests/data/test23 b/tests/data/test23 index 1771fe9e1583..8126025b9063 100644 --- a/tests/data/test23 +++ b/tests/data/test23 @@ -3,7 +3,6 @@ unsupported scheme -FAILURE # Server-side diff --git a/tests/data/test2302 b/tests/data/test2302 index 397350dc99ae..396ba16a7ecc 100644 --- a/tests/data/test2302 +++ b/tests/data/test2302 @@ -6,7 +6,7 @@ WebSockets -# Sends a PING + a 5 byte hello TEXT +# Sends a PING + a 5-byte hello TEXT HTTP/1.1 101 Switching to WebSockets diff --git a/tests/data/test2304 b/tests/data/test2304 index 26bbda187e6a..aab6dc822b48 100644 --- a/tests/data/test2304 +++ b/tests/data/test2304 @@ -62,7 +62,7 @@ Connection: Upgrade # This test used to check that "connection closed" was output, but -# that is flaky since the outgoing PING just before might fail already +# that is flaky since the outgoing PING before might fail already # and then the test exists before the output gets to be written diff --git a/tests/data/test2310 b/tests/data/test2310 new file mode 100644 index 000000000000..0446c374a0ef --- /dev/null +++ b/tests/data/test2310 @@ -0,0 +1,50 @@ + + + + +variables + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: something-close, close-something, close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# Client-side + + +http + + +variable decode and trim + + +--variable 'VAR=IA==' --expand-url 'http://%HOSTIP:%HTTPPORT/{{VAR:64dec:trim}}' + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test2311 b/tests/data/test2311 new file mode 100644 index 000000000000..91032b81d241 --- /dev/null +++ b/tests/data/test2311 @@ -0,0 +1,58 @@ + + + + +HTTP +HTTP GET +HTTP proxy +cookies + + + +# Server-side + + +HTTP/1.1 200 OK +Server: test-server/fake +Content-Length: 21 + +This server says moo + + + +# Client-side + + +http + + +Cookie from file: control octet rejected, prefixes matched case sensitively + + +http://example.fake/%TESTNUMBER -b %LOGDIR/injar%TESTNUMBER -x %HOSTIP:%HTTPPORT + + +example.fake FALSE / FALSE 0 clean good +example.fake FALSE / FALSE 0 bad %hex[ba%07d]hex% +example.fake FALSE / FALSE 0 __secure-x yes +example.fake FALSE / FALSE 0 __Secure-y no + + +cookies +proxy + + + +# Verify data after the test has been "shot" + + +GET http://example.fake/%TESTNUMBER HTTP/1.1 +Host: example.fake +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive +Cookie: __secure-x=yes; clean=good + + + + diff --git a/tests/data/test2408 b/tests/data/test2408 new file mode 100644 index 000000000000..edc83d48a4ec --- /dev/null +++ b/tests/data/test2408 @@ -0,0 +1,92 @@ + + + + +HTTP +HTTP GET +globbing +{} list + + +# Server-side + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +moo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +foo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +hoo + + + +# Client-side + + +http + + +multiple requests using named {} globs in URL + + +"%HOSTIP:%HTTPPORT/{%LTtest%GT%TESTNUMBER,%TESTNUMBER0002,%TESTNUMBER0003}" -o "%LOGDIR/dump-#%LTtest%GT" + + + +# Verify data after the test has been "shot" + + +GET /%TESTNUMBER HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0003 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +moo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +foo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +hoo + + + + diff --git a/tests/data/test2409 b/tests/data/test2409 new file mode 100644 index 000000000000..4c9e9c57a175 --- /dev/null +++ b/tests/data/test2409 @@ -0,0 +1,92 @@ + + + + +HTTP +HTTP GET +globbing +{} list + + +# Server-side + + +HTTP/1.1 200 swsbounce +Funny-head: yesyes +Content-Length: 4 + +moo + + +HTTP/1.1 200 swsbounce +Funny-head: yesyes +Content-Length: 4 + +foo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +hoo + + + +# Client-side + + +http + + +multiple requests using named [] globs in URL + + +"%HOSTIP:%HTTPPORT/hello[%LTtest%GT7-9]" -o "%LOGDIR/dump-#%LTtest%GT" + + + +# Verify data after the test has been "shot" + + +GET /hello7 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /hello8 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /hello9 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + + + + +HTTP/1.1 200 swsbounce +Funny-head: yesyes +Content-Length: 4 + +moo + + +HTTP/1.1 200 swsbounce +Funny-head: yesyes +Content-Length: 4 + +foo + + +HTTP/1.1 200 OK +Funny-head: yesyes +Content-Length: 4 + +hoo + + + + diff --git a/tests/data/test2410 b/tests/data/test2410 new file mode 100644 index 000000000000..ce83263e532d --- /dev/null +++ b/tests/data/test2410 @@ -0,0 +1,34 @@ + + + + +HTTP +HTTP GET +globbing +{} list + + +# Server-side + +# Client-side + + +duplicate named glob + + +"https://dummy.example/{%LTtest%GTA,B}{%LTtest%GTC,D}" -o "%LOGDIR/dump" + + + +# Verify data after the test has been "shot" + + +curl: (3) Duplicate glob name in position 40: +https://dummy.example/{%LTtest%GTA,B}{%LTtest%GTC,D} + ^ + + +3 + + + diff --git a/tests/data/test2411 b/tests/data/test2411 new file mode 100644 index 000000000000..5f45ac6ad8cf --- /dev/null +++ b/tests/data/test2411 @@ -0,0 +1,37 @@ + + + + +HTTP +HTTP GET +globbing +{} list + + +# Server-side + +# Client-side + + +http + + +reference a named glob not set + + +"%HOSTIP:%HTTPPORT/{%LTtest%GTA,B}{%LTmoo%GTC,D}" -o "somewhere/#%LTfoo%GT" + + + +# Verify data after the test has been "shot" + + +curl: (43) no glob exists with this name in position 16: +somewhere/#%LTfoo%GT + ^ + + +43 + + + diff --git a/tests/data/test2412 b/tests/data/test2412 new file mode 100644 index 000000000000..e0320e2ce4b6 --- /dev/null +++ b/tests/data/test2412 @@ -0,0 +1,50 @@ + + + + +multi + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6007 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- +%repeat[1000 x foobar]% + + + +# Client-side + + +wakeup + + +http + + +lib%TESTNUMBER + + +checking curl_multi_fdset on nothing to do + + +http://%HOSTIP:%HTTPPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + + diff --git a/tests/data/test1609 b/tests/data/test2413 similarity index 82% rename from tests/data/test1609 rename to tests/data/test2413 index 5153174a3afe..0b0e8a08aacc 100644 --- a/tests/data/test1609 +++ b/tests/data/test2413 @@ -3,7 +3,7 @@ unittest -CURLOPT_RESOLVE +Curl_peer @@ -13,7 +13,7 @@ CURLOPT_RESOLVE unittest -CURLOPT_RESOLVE parsing +Curl_peer unit tests diff --git a/tests/data/test2500 b/tests/data/test2500 index 2478245a8cc9..2abed30416f2 100644 --- a/tests/data/test2500 +++ b/tests/data/test2500 @@ -5,6 +5,7 @@ HTTP HTTP GET HTTP/3 +--resolve diff --git a/tests/data/test2503 b/tests/data/test2503 index 5ade56188553..934798d33627 100644 --- a/tests/data/test2503 +++ b/tests/data/test2503 @@ -5,6 +5,7 @@ HTTP HTTP/3 HTTPS +--resolve -w %header diff --git a/tests/data/test2504 b/tests/data/test2504 index 8cec1c8210f2..7bb4a81ed7e6 100644 --- a/tests/data/test2504 +++ b/tests/data/test2504 @@ -34,6 +34,9 @@ custom Host with cookie, handle reuse, no custom Host: http://%HOSTIP:%HTTPPORT + +cookies + # Verify data after the test has been "shot" diff --git a/tests/data/test256 b/tests/data/test256 index 3bf44c5770f9..cc4d7e209dc4 100644 --- a/tests/data/test256 +++ b/tests/data/test256 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy Resume -FAILURE # Server-side diff --git a/tests/data/test2713 b/tests/data/test2713 index 6ec722ceae0c..6c50bc72fe06 100644 --- a/tests/data/test2713 +++ b/tests/data/test2713 @@ -34,7 +34,7 @@ upgrade # Full list of frames: see 'verify.stdout' below -# A 126 byte long PING +# A 126-byte-long PING HTTP/1.1 101 Switching to WebSockets Server: server/%TESTNUMBER diff --git a/tests/data/test2714 b/tests/data/test2714 index 60c5c6daf739..654004966f31 100644 --- a/tests/data/test2714 +++ b/tests/data/test2714 @@ -34,7 +34,7 @@ upgrade # Full list of frames: see 'verify.stdout' below -# A 126 byte long PONG +# A 126-byte-long PONG HTTP/1.1 101 Switching to WebSockets Server: server/%TESTNUMBER diff --git a/tests/data/test2715 b/tests/data/test2715 index 68ea8647abe6..4cf4febb3532 100644 --- a/tests/data/test2715 +++ b/tests/data/test2715 @@ -34,7 +34,7 @@ upgrade # Full list of frames: see 'verify.stdout' below -# A 126 byte long CLOSE +# A 126-byte-long CLOSE HTTP/1.1 101 Switching to WebSockets Server: server/%TESTNUMBER diff --git a/tests/data/test283 b/tests/data/test283 index 3ca5bd1e0d62..051e58c87c8b 100644 --- a/tests/data/test283 +++ b/tests/data/test283 @@ -4,7 +4,6 @@ TFTP TFTP RRQ -FAILURE diff --git a/tests/data/test284 b/tests/data/test284 index 469172a5dc96..aa48f4388cfa 100644 --- a/tests/data/test284 +++ b/tests/data/test284 @@ -11,7 +11,7 @@ TFTP RRQ A chunk of data which exactly fits into -a 512 byte TFTP block, testing a boundary +a 512-byte TFTP block, testing a boundary condition in the TFTP receive code. 789ABCDEF 123456789ABCDEF @@ -47,7 +47,7 @@ condition in the TFTP receive code. tftp -TFTP retrieve of boundary case 512 byte file +TFTP retrieve of boundary case 512-byte file tftp://%HOSTIP:%TFTPPORT//%TESTNUMBER diff --git a/tests/data/test286 b/tests/data/test286 index 9902d160b835..cc9fd7c8f62e 100644 --- a/tests/data/test286 +++ b/tests/data/test286 @@ -13,14 +13,14 @@ TFTP WRQ tftp -TFTP send of boundary case 512 byte file +TFTP send of boundary case 512-byte file -T %LOGDIR/test%TESTNUMBER.txt tftp://%HOSTIP:%TFTPPORT// A chunk of data which exactly fits into -a 512 byte TFTP block, testing a boundary +a 512-byte TFTP block, testing a boundary condition in the TFTP transmit code. 89ABCDEF 123456789ABCDEF @@ -54,7 +54,7 @@ condition in the TFTP transmit code. A chunk of data which exactly fits into -a 512 byte TFTP block, testing a boundary +a 512-byte TFTP block, testing a boundary condition in the TFTP transmit code. 89ABCDEF 123456789ABCDEF diff --git a/tests/data/test289 b/tests/data/test289 index cf8464b1b629..4253c1b77604 100644 --- a/tests/data/test289 +++ b/tests/data/test289 @@ -5,7 +5,6 @@ FTP STOR Resume -FAILURE diff --git a/tests/data/test29 b/tests/data/test29 index 122cfd23b73f..df67f850197f 100644 --- a/tests/data/test29 +++ b/tests/data/test29 @@ -5,7 +5,6 @@ HTTP HTTP GET timeout -FAILURE # Server-side diff --git a/tests/data/test293 b/tests/data/test293 index cc6cccb41ee1..c6f69f2e0105 100644 --- a/tests/data/test293 +++ b/tests/data/test293 @@ -5,7 +5,6 @@ HTTP HTTP GET --max-filesize -FAILURE diff --git a/tests/data/test295 b/tests/data/test295 index 1eb14475fb98..e7fbf6745c4b 100644 --- a/tests/data/test295 +++ b/tests/data/test295 @@ -6,7 +6,6 @@ FTP PASV LIST ACCT -FAILURE # Server-side diff --git a/tests/data/test30 b/tests/data/test30 index c8984105f83f..c0b5d60d431d 100644 --- a/tests/data/test30 +++ b/tests/data/test30 @@ -4,7 +4,6 @@ HTTP HTTP GET -FAILURE # Server-side diff --git a/tests/data/test3016 b/tests/data/test3016 index 2d530f55c026..c710e3976283 100644 --- a/tests/data/test3016 +++ b/tests/data/test3016 @@ -2,8 +2,6 @@ -HTTP -HTTP GET FILE @@ -14,7 +12,7 @@ FILE file -GET a directory using file:// +Get current directory using file:// diff --git a/tests/data/test302 b/tests/data/test302 index 1bae0c76410f..3c6eb09851eb 100644 --- a/tests/data/test302 +++ b/tests/data/test302 @@ -6,7 +6,6 @@ HTTPS HTTP GET HTTP CONNECT HTTP proxy -FAILURE diff --git a/tests/data/test3021 b/tests/data/test3021 index b7fe479cb2bc..1aa973a68843 100644 --- a/tests/data/test3021 +++ b/tests/data/test3021 @@ -17,10 +17,6 @@ test # Client-side -# so far only the libssh2 backend supports SHA256 - -libssh2 - sftp diff --git a/tests/data/test3022 b/tests/data/test3022 index 057242b0f513..6d692a817ef3 100644 --- a/tests/data/test3022 +++ b/tests/data/test3022 @@ -17,10 +17,6 @@ test # Client-side -# so far only the libssh2 backend supports SHA256 - -libssh2 - scp diff --git a/tests/data/test303 b/tests/data/test303 index 44060b496e79..51dc471e61a1 100644 --- a/tests/data/test303 +++ b/tests/data/test303 @@ -5,7 +5,6 @@ HTTPS HTTP GET timeout -FAILURE diff --git a/tests/data/test3031 b/tests/data/test3031 index bf41ac78dbf7..68ffb466919d 100644 --- a/tests/data/test3031 +++ b/tests/data/test3031 @@ -32,7 +32,7 @@ http http ---output-dir with --create-dirs +--dump-header with --create-dirs http://%HOSTIP:%HTTPPORT/this/is/the/%TESTNUMBER --dump-header %PWD/%LOGDIR/tmp/out.txt --create-dirs diff --git a/tests/data/test305 b/tests/data/test305 index 57d33938e118..1acefe29be4c 100644 --- a/tests/data/test305 +++ b/tests/data/test305 @@ -4,7 +4,6 @@ HTTPS HTTP GET -FAILURE diff --git a/tests/data/test306 b/tests/data/test306 index 5ef7a59010bc..b0974a77a36f 100644 --- a/tests/data/test306 +++ b/tests/data/test306 @@ -10,7 +10,7 @@ HTTP GET # Server-side -No headers at all, just data swsclose +No headers at all, data swsclose Let's get diff --git a/tests/data/test308 b/tests/data/test308 index 60274f489d75..593976b4c239 100644 --- a/tests/data/test308 +++ b/tests/data/test308 @@ -4,7 +4,6 @@ HTTPS HTTP GET -FAILURE diff --git a/tests/data/test31 b/tests/data/test31 index 92cc58ca2efa..c885b7c9faed 100644 --- a/tests/data/test31 +++ b/tests/data/test31 @@ -6,6 +6,7 @@ HTTP HTTP GET cookies cookiejar +--resolve # Server-side diff --git a/tests/data/test310 b/tests/data/test310 index 558f137d8ac4..12a12a4f1503 100644 --- a/tests/data/test310 +++ b/tests/data/test310 @@ -31,7 +31,7 @@ local-http https test-localhost.pem -simple HTTPS GET +HTTPS GET with specified CA cert bundle -4 --cacert %CERTDIR/certs/test-ca.crt https://localhost:%HTTPSPORT/%TESTNUMBER diff --git a/tests/data/test3106 b/tests/data/test3106 new file mode 100644 index 000000000000..971107e0fa87 --- /dev/null +++ b/tests/data/test3106 @@ -0,0 +1,77 @@ + + + + +HTTP +HTTPS +HTTP proxy +HTTP Basic auth +followlocation + + + +# Server-side + + +HTTP/1.1 200 OK + + + + +HTTP/1.1 302 Found +Location: http://example.com:%HTTPSPORT/%TESTNUMBER0002 +Content-Length: 0 + + + + +HTTP/1.1 200 OK +Content-Length: 2 + +OK + + + +# Client-side + + +SSL +proxy + + +https +http-proxy + + +HTTPS to HTTP redirect on same host and port without auth + + +--insecure --location --user user:secret --proxy %HOSTIP:%PROXYPORT https://example.com:%HTTPSPORT/%TESTNUMBER + + + +# Verify data after the test has been "shot" + + +CONNECT example.com:%HTTPSPORT HTTP/1.1 +Host: example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Proxy-Connection: Keep-Alive + +GET http://example.com:%HTTPSPORT/%TESTNUMBER0002 HTTP/1.1 +Host: example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + +GET /%TESTNUMBER HTTP/1.1 +Host: example.com:%HTTPSPORT +Authorization: Basic %b64[user:secret]b64% +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test315 b/tests/data/test315 index 62499204a2ef..31d812cd05f9 100644 --- a/tests/data/test315 +++ b/tests/data/test315 @@ -5,7 +5,6 @@ HTTP HTTP GET compressed -FAILURE # Server-side diff --git a/tests/data/test3201 b/tests/data/test3201 index 7e051a216729..cf98f4f0a394 100644 --- a/tests/data/test3201 +++ b/tests/data/test3201 @@ -33,7 +33,7 @@ Funny-head: barkbark http -HTTP GET when PROXY Protocol enabled and spoofed client IP +HTTP GET when PROXY Protocol enabled and spoofed client IPv4 http://%HOSTIP:%HTTPPORT/%TESTNUMBER --haproxy-clientip "192.168.1.1" -H "Testno: %TESTNUMBER" diff --git a/tests/data/test3203 b/tests/data/test3203 index 6a548140604a..401f69741976 100644 --- a/tests/data/test3203 +++ b/tests/data/test3203 @@ -2,8 +2,6 @@ -HTTP -HTTP GET FILE @@ -14,7 +12,7 @@ FILE file -GET a directory using file:// +Get a directory using file:// diff --git a/tests/data/test3207 b/tests/data/test3207 index 521a1047f06c..ba15b1a47975 100644 --- a/tests/data/test3207 +++ b/tests/data/test3207 @@ -32,7 +32,7 @@ OpenSSL https -concurrent HTTPS GET using shared ssl session cache +concurrent HTTPS GET using shared SSL session cache lib%TESTNUMBER diff --git a/tests/data/test321 b/tests/data/test321 index cdc18028b9a0..ca258c078d79 100644 --- a/tests/data/test321 +++ b/tests/data/test321 @@ -4,7 +4,6 @@ HTTPS TLS-SRP -FAILURE diff --git a/tests/data/test3212 b/tests/data/test3212 index 826c961a4d63..3c5cd37e7f51 100644 --- a/tests/data/test3212 +++ b/tests/data/test3212 @@ -3,7 +3,7 @@ unittest -uint_bset +uint32_tbl @@ -13,7 +13,7 @@ uint_bset unittest -uint_bset unit tests +uint32_tbl unit tests diff --git a/tests/data/test3217 b/tests/data/test3217 index df7c890eba72..4749779b6753 100644 --- a/tests/data/test3217 +++ b/tests/data/test3217 @@ -5,7 +5,6 @@ FTP PASV RETR -FAILURE # Server-side diff --git a/tests/data/test3218 b/tests/data/test3218 index 0c98b202eca5..a56b9a2b98e6 100644 --- a/tests/data/test3218 +++ b/tests/data/test3218 @@ -5,7 +5,6 @@ FTP PASV RETR -FAILURE # Server-side diff --git a/tests/data/test322 b/tests/data/test322 index c0581088ae6c..e0aca6b28eda 100644 --- a/tests/data/test322 +++ b/tests/data/test322 @@ -4,7 +4,6 @@ HTTPS TLS-SRP -FAILURE diff --git a/tests/data/test3220 b/tests/data/test3220 index ba8dec8f923f..ca8bfaa134b0 100644 --- a/tests/data/test3220 +++ b/tests/data/test3220 @@ -33,7 +33,7 @@ Funny-head: barkbark http -HTTP GET when PROXY Protocol enabled and spoofed client IP +HTTP GET when PROXY Protocol enabled and spoofed client IPv6 http://%HOSTIP:%HTTPPORT/%TESTNUMBER --haproxy-clientip "2a04:4e42::347" -H "Testno: %TESTNUMBER" diff --git a/tests/data/test3221 b/tests/data/test3221 new file mode 100644 index 000000000000..321213ab0919 --- /dev/null +++ b/tests/data/test3221 @@ -0,0 +1,74 @@ + + + + +HTTP +HTTP GET +digest + + + +# Server-side + + +HTTP/1.1 401 Authorization Required +WWW-Authenticate: Digest realm="testrealm%0a%0d", nonce="1053604145" +Content-Length: 4 + +hej + + + +HTTP/1.1 200 OK +Content-Length: 23 + +This IS the real page! + + + +HTTP/1.1 401 Authorization Required +WWW-Authenticate: Digest realm="testrealm%0a%0d", nonce="1053604145" +Content-Length: 4 + +HTTP/1.1 200 OK +Content-Length: 23 + +This IS the real page! + + + +# Client-side + + +http + + +!SSPI +crypto +digest + + +HTTP Digest with CRLF in username + + +http://hello%0a%0d:there@%HOSTIP:%HTTPPORT/ --digest + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* + +GET / HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Authorization: Digest username="hello%0A%0D", realm="testrealm%0a%0d", nonce="1053604145", uri="/", response="64e5ae1b90f05309847ac483c1094284" +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test3222 b/tests/data/test3222 new file mode 100644 index 000000000000..cf6caa157f80 --- /dev/null +++ b/tests/data/test3222 @@ -0,0 +1,57 @@ + + + + +HTTP +aws-sigv4 + + + +# Server-side + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT +ETag: "21025-dc7-39462498" +Accept-Ranges: bytes +Content-Length: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + +# Client-side + + +http + + +Debug +aws + + +aws-sigv4 with CRLF in username + + +"http://user%0d%0a:secret@fake.fake.fake:8000/" --aws-sigv4 "aws:amz:us-east-2:es" --connect-to fake.fake.fake:8000:%HOSTIP:%HTTPPORT + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: fake.fake.fake:8000 +Authorization: AWS4-HMAC-SHA256 Credential=user%0D%0A/19700101/us-east-2/es/aws4_request, SignedHeaders=host;x-amz-date, Signature=e5747e9555c0e96f1067cc4bf9f6055e72a185178e5dd0c2909279ec1d66360b +X-Amz-Date: 19700101T000000Z +User-Agent: curl/%VERSION +Accept: */* + + + + diff --git a/tests/data/test323 b/tests/data/test323 index e8f45c5ef8fe..ba09253c0f17 100644 --- a/tests/data/test323 +++ b/tests/data/test323 @@ -4,7 +4,6 @@ HTTPS TLS-SRP -FAILURE diff --git a/tests/data/test324 b/tests/data/test324 index a91301fe1d8a..d85fecb9b33d 100644 --- a/tests/data/test324 +++ b/tests/data/test324 @@ -4,7 +4,6 @@ HTTPS TLS-SRP -FAILURE diff --git a/tests/data/test3302 b/tests/data/test3302 new file mode 100644 index 000000000000..35ccfc79af00 --- /dev/null +++ b/tests/data/test3302 @@ -0,0 +1,19 @@ + + + + +unittest +CURLOPT_GSSAPI_DELEGATION + + + +# Client-side + + +unittest + + +CURLOPT_GSSAPI_DELEGATION stores flags in data->set on GSS-API and SSPI builds + + + diff --git a/tests/data/test3303 b/tests/data/test3303 new file mode 100644 index 000000000000..697049f013cc --- /dev/null +++ b/tests/data/test3303 @@ -0,0 +1,20 @@ + + + + +unittest +TLS +mTLS + + + +# Client-side + + +unittest + + +conn-reuse match distinguishes mTLS key, cert_type, key_type and key_passwd fields + + + diff --git a/tests/data/test3304 b/tests/data/test3304 new file mode 100644 index 000000000000..4380c0819f02 --- /dev/null +++ b/tests/data/test3304 @@ -0,0 +1,20 @@ + + + + +unittest +TLS +mTLS + + + +# Client-side + + +unittest + + +TLS session cache peer key discriminates on mTLS key, key_type and cert_type fields + + + diff --git a/tests/data/test3305 b/tests/data/test3305 new file mode 100644 index 000000000000..7a6d63beeeca --- /dev/null +++ b/tests/data/test3305 @@ -0,0 +1,84 @@ + + + + +HTTP +cookies +--resolve + + + +# Server-side + + +HTTP/1.1 301 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Set-Cookie: this=secret; domain=example.com; secure; path=/ +Set-Cookie: that=secret; domain=www.example.com; secure; path=/ +Set-Cookie: second=fine; + +-foo- + + +# The cookie 'this' should not be accepted since it would be the same as already +# set with a 'secure' flag. +# The cookie 'second' is however not secure so it is fair game to override + +HTTP/1.1 301 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Content-Length: 6 +Set-Cookie: this=open; path=/ +Set-Cookie: that=open; path=/; domain=example.com +Set-Cookie: second=override + +-foo- + + + +HTTP/1.1 200 OK +Date: Tue, 09 Nov 2010 14:49:00 GMT +Server: test-server/fake +Content-Length: 6 + +-foo- + + + +# Client-side + + +http +https + + +same-name cookie over HTTPS and HTTP with different domains + + +https://www.example.com:%HTTPSPORT/ http://www.example.com:%HTTPPORT/%TESTNUMBER0002 https://www.example.com:%HTTPSPORT/%TESTNUMBER0003 --insecure -c %LOGDIR/cookie%TESTNUMBER --resolve www.example.com:%HTTPSPORT:%HOSTIP --resolve www.example.com:%HTTPPORT:%HOSTIP + + + +# Verify data after the test has been "shot" + + +GET / HTTP/1.1 +Host: www.example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* + +GET /%TESTNUMBER0002 HTTP/1.1 +Host: www.example.com:%HTTPPORT +User-Agent: curl/%VERSION +Accept: */* +Cookie: second=fine + +GET /%TESTNUMBER0003 HTTP/1.1 +Host: www.example.com:%HTTPSPORT +User-Agent: curl/%VERSION +Accept: */* +Cookie: second=override; that=secret; this=secret + + + + diff --git a/tests/data/test332 b/tests/data/test332 index b7a7e6000df3..e968c26fe4f8 100644 --- a/tests/data/test332 +++ b/tests/data/test332 @@ -4,7 +4,6 @@ TFTP TFTP RRQ -FAILURE diff --git a/tests/data/test3400 b/tests/data/test3400 new file mode 100644 index 000000000000..12d014bffd21 --- /dev/null +++ b/tests/data/test3400 @@ -0,0 +1,19 @@ + + + + +unittest +capsule + + + + + +unittest + + +capsule protocol encode and decode unit tests + + + + diff --git a/tests/data/test3401 b/tests/data/test3401 new file mode 100644 index 000000000000..0c88738ec206 --- /dev/null +++ b/tests/data/test3401 @@ -0,0 +1,55 @@ + + + + +HTTP +HTTPS proxy +cookies +Secure + + + +# Server-side + + +HTTP/1.1 200 OK +Content-Length: 4 + +foo + + + +# Client-side + + +http +https-proxy + + +HTTPS-proxy +cookies + + +HTTP via HTTPS proxy does not send Secure cookies + + +-x https://%HOSTIP:%HTTPSPROXYPORT --proxy-insecure -b %LOGDIR/jar%TESTNUMBER.txt http://test.example/%TESTNUMBER + + +# Netscape HTTP Cookie File +test.example FALSE / TRUE 9999999999 session secret + + + +# Verify data after the test has been "shot" + + +GET http://test.example/%TESTNUMBER HTTP/1.1 +Host: test.example +User-Agent: curl/%VERSION +Accept: */* +Proxy-Connection: Keep-Alive + + + + diff --git a/tests/data/test341 b/tests/data/test341 index 8aea4fbb7ce4..0d204a4f1310 100644 --- a/tests/data/test341 +++ b/tests/data/test341 @@ -35,7 +35,7 @@ chunky-trailer: header data http -A non existing file with --etag-compare is just a blank +A non existing file with --etag-compare is a blank http://%HOSTIP:%HTTPPORT/%TESTNUMBER --etag-compare %LOGDIR/etag%TESTNUMBER diff --git a/tests/data/test357 b/tests/data/test357 index a763fd22556d..c6b11bd83c75 100644 --- a/tests/data/test357 +++ b/tests/data/test357 @@ -48,6 +48,9 @@ no-expect http + +!torture + HTTP PUT with Expect: 100-continue and 417 response diff --git a/tests/data/test358 b/tests/data/test358 index ac7f3d57e421..99035cc049f8 100644 --- a/tests/data/test358 +++ b/tests/data/test358 @@ -36,7 +36,7 @@ http http/2 -HTTPS GET translated by alt-svc lookup to HTTP/2 GET +HTTP GET translated by alt-svc lookup to HTTP/2 GET # make Debug-curl accept Alt-Svc over plain HTTP diff --git a/tests/data/test36 b/tests/data/test36 index ea97f6160b99..a99e560bdcde 100644 --- a/tests/data/test36 +++ b/tests/data/test36 @@ -5,7 +5,6 @@ HTTP HTTP GET chunked Transfer-Encoding -FAILURE # Server-side diff --git a/tests/data/test37 b/tests/data/test37 index cf455095475e..46f994f1e787 100644 --- a/tests/data/test37 +++ b/tests/data/test37 @@ -4,7 +4,6 @@ HTTP HTTP GET -FAILURE # Server-side diff --git a/tests/data/test38 b/tests/data/test38 index 842b0e535e7d..05afab2829f8 100644 --- a/tests/data/test38 +++ b/tests/data/test38 @@ -5,7 +5,6 @@ HTTP HTTP GET Resume -FAILURE # Server-side diff --git a/tests/data/test387 b/tests/data/test387 index ebdd4b5d116f..5872cd692e11 100644 --- a/tests/data/test387 +++ b/tests/data/test387 @@ -51,7 +51,7 @@ Connection: TE 61 -curl: (61) Reject response due to more than 5 content encodings +curl: (61) Reject response exceeding limit of 5 transfer encodings diff --git a/tests/data/test388 b/tests/data/test388 index 7994eaf8ab05..b9f33e2d2575 100644 --- a/tests/data/test388 +++ b/tests/data/test388 @@ -56,7 +56,7 @@ Content-Length: 26 This is not the real page -# The second request to the 1002 section will bounce this one back instead +# The second request to the 1002 section bounces this one back instead # thanks to the swsbounce keyword up there HTTP/1.1 200 OK diff --git a/tests/data/test390 b/tests/data/test390 index 7a5faaf8361b..0489d9be8662 100644 --- a/tests/data/test390 +++ b/tests/data/test390 @@ -3,6 +3,7 @@ HTTP +FILE FTP parallel diff --git a/tests/data/test393 b/tests/data/test393 index fbf07d7c601d..3353da688a37 100644 --- a/tests/data/test393 +++ b/tests/data/test393 @@ -5,7 +5,6 @@ HTTP HTTP GET --max-filesize -FAILURE diff --git a/tests/data/test394 b/tests/data/test394 index 3a51d5a9c594..c3cbc691eb3c 100644 --- a/tests/data/test394 +++ b/tests/data/test394 @@ -4,7 +4,6 @@ HTTP HTTP GET -FAILURE diff --git a/tests/data/test399 b/tests/data/test399 index cc4d0fc47428..6984fbd85f3c 100644 --- a/tests/data/test399 +++ b/tests/data/test399 @@ -12,7 +12,7 @@ URL http -65536 bytes long hostname in URL +65536-byte-long hostname in URL url = http://%repeat[65536 x a]%/399 diff --git a/tests/data/test402 b/tests/data/test402 index 5226d6c731b2..ab036b27d645 100644 --- a/tests/data/test402 +++ b/tests/data/test402 @@ -4,7 +4,6 @@ FTP FTPS -FAILURE diff --git a/tests/data/test403 b/tests/data/test403 index bbf162d22c71..6bf16a62305d 100644 --- a/tests/data/test403 +++ b/tests/data/test403 @@ -7,7 +7,6 @@ FTPS PASV LIST CCC -FAILURE # Server-side diff --git a/tests/data/test404 b/tests/data/test404 index 96f3eaa9c09e..1e69b881f7ea 100644 --- a/tests/data/test404 +++ b/tests/data/test404 @@ -4,7 +4,6 @@ FTP FTPS -FAILURE diff --git a/tests/data/test405 b/tests/data/test405 index 73a6dcf430ad..e67dcf16d4b0 100644 --- a/tests/data/test405 +++ b/tests/data/test405 @@ -4,7 +4,6 @@ FTP FTPS -FAILURE diff --git a/tests/data/test409 b/tests/data/test409 deleted file mode 100644 index 5660b5d6ca84..000000000000 --- a/tests/data/test409 +++ /dev/null @@ -1,58 +0,0 @@ - - - - -FTP -FTPS -EPSV -STOR - - - -# Client-side - - -SSL - - -ftps - - -FTPS PASV upload file - - -data - to - see -that FTP -works - so does it? - - ---insecure --ftp-ssl-control ftps://%HOSTIP:%FTPSPORT/%TESTNUMBER -T %LOGDIR/test%TESTNUMBER.txt - - - -# Verify data after the test has been "shot" - - -data - to - see -that FTP -works - so does it? - - -USER anonymous -PASS ftp@example.com -PBSZ 0 -PROT C -PWD -EPSV -TYPE I -STOR %TESTNUMBER -QUIT - - - diff --git a/tests/data/test41 b/tests/data/test41 index efeb58ec9b65..0259f25bb94f 100644 --- a/tests/data/test41 +++ b/tests/data/test41 @@ -4,7 +4,6 @@ HTTP HTTP FORMPOST -FAILURE # Server-side diff --git a/tests/data/test418 b/tests/data/test418 index ccda8912298d..cdd25f4ba9bc 100644 --- a/tests/data/test418 +++ b/tests/data/test418 @@ -59,7 +59,7 @@ Connection: TE 61 -curl: (61) Reject response due to more than 5 content encodings +curl: (61) Reject response exceeding limit of 5 transfer encodings diff --git a/tests/data/test419 b/tests/data/test419 index 51c5a16a0810..04a6a63d9f89 100644 --- a/tests/data/test419 +++ b/tests/data/test419 @@ -3,7 +3,6 @@ --dump-header -FAILURE diff --git a/tests/data/test472 b/tests/data/test472 index dd426b9cecf1..f2e96717f78d 100644 --- a/tests/data/test472 +++ b/tests/data/test472 @@ -36,7 +36,7 @@ Unicode aws -aws-sigv4 with query +aws-sigv4 with query using unicode "http://fake.fake.fake:8000/%TESTNUMBER/a=%hex[%e3%81%82]hex%" -u user:secret --aws-sigv4 "aws:amz:us-east-2:es" --connect-to fake.fake.fake:8000:%HOSTIP:%HTTPPORT diff --git a/tests/data/test504 b/tests/data/test504 index 457dfb6d3848..c4670ee1b174 100644 --- a/tests/data/test504 +++ b/tests/data/test504 @@ -6,7 +6,6 @@ HTTP HTTP GET HTTP proxy multi -FAILURE connect to non-listen diff --git a/tests/data/test507 b/tests/data/test507 index c06743f30c9d..5915ef70422b 100644 --- a/tests/data/test507 +++ b/tests/data/test507 @@ -4,7 +4,6 @@ HTTP multi -FAILURE non-existing host diff --git a/tests/data/test527 b/tests/data/test527 index bcc0d8781844..735bd20e8528 100644 --- a/tests/data/test527 +++ b/tests/data/test527 @@ -31,7 +31,7 @@ ftp lib526 -FTP RETR same file using different handles but same connection +FTP RETR same file using different handles and closed connections ftp://%HOSTIP:%FTPPORT/path/%TESTNUMBER diff --git a/tests/data/test536 b/tests/data/test536 index b03bc52318f1..e0bcc906f7de 100644 --- a/tests/data/test536 +++ b/tests/data/test536 @@ -43,7 +43,7 @@ CURLINFO_USED_PROXY # 1 - the non-proxy using URL # 2 - the CURLOPT_RESOLVE string to change IP for the name -http://%HOSTIP:%HTTPPORT goingdirect.com:%HTTPPORT goingdirect.com:%HTTPPORT:%HOSTIP +http://%HOSTIP:%HTTPPORT goingdirect.test:%HTTPPORT goingdirect.test:%HTTPPORT:%HOSTIP proxy @@ -53,13 +53,13 @@ proxy # Verify data after the test has been "shot" -GET http://usingproxy.com/ HTTP/1.1 -Host: usingproxy.com +GET http://usingproxy.test/ HTTP/1.1 +Host: usingproxy.test Accept: */* Proxy-Connection: Keep-Alive GET / HTTP/1.1 -Host: goingdirect.com:%HTTPPORT +Host: goingdirect.test:%HTTPPORT Accept: */* diff --git a/tests/data/test538 b/tests/data/test538 index 2ee171b5b1a0..69e6fa931c35 100644 --- a/tests/data/test538 +++ b/tests/data/test538 @@ -3,7 +3,6 @@ FTP -FAILURE multi diff --git a/tests/data/test583 b/tests/data/test583 index 9463cd887fc5..8428d31395c9 100644 --- a/tests/data/test583 +++ b/tests/data/test583 @@ -25,7 +25,7 @@ lib%TESTNUMBER SFTP with multi interface, remove handle early -# The command here uses 'localhost' just to make sure that curl_multi_perform +# The command here uses 'localhost' to make sure that curl_multi_perform # does not reach too far in the first invoke. When using c-ares at least, the # name resolve causes it to return rather quickly and thus we could trigger # the problem we are looking to verify. diff --git a/tests/data/test594 b/tests/data/test594 index 98db071f2649..aaa007cbde8a 100644 --- a/tests/data/test594 +++ b/tests/data/test594 @@ -12,7 +12,6 @@ multi EPRT refused NODATACONN timeout -FAILURE diff --git a/tests/data/test604 b/tests/data/test604 index e6d6371945d5..c901cb6fbf2e 100644 --- a/tests/data/test604 +++ b/tests/data/test604 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test605 b/tests/data/test605 index ea7af94af466..ba708afeffbb 100644 --- a/tests/data/test605 +++ b/tests/data/test605 @@ -3,7 +3,6 @@ SCP -FAILURE diff --git a/tests/data/test606 b/tests/data/test606 index 48286e63e6c9..95e9f3d6b3cf 100644 --- a/tests/data/test606 +++ b/tests/data/test606 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test607 b/tests/data/test607 index 92ffe48e5596..960cb0b2aaaa 100644 --- a/tests/data/test607 +++ b/tests/data/test607 @@ -3,7 +3,6 @@ SCP -FAILURE diff --git a/tests/data/test608 b/tests/data/test608 index 5ac7a67d96c0..9e35595e763f 100644 --- a/tests/data/test608 +++ b/tests/data/test608 @@ -20,7 +20,7 @@ Test file for rename test sftp -SFTP post-quote rename +SFTP post-quote rename a file --key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: -Q "-rename %SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt %SFTP_PWD/%LOGDIR/file%TESTNUMBER-renamed.txt" sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt --insecure diff --git a/tests/data/test609 b/tests/data/test609 index efd8496f3264..c5c7335f735d 100644 --- a/tests/data/test609 +++ b/tests/data/test609 @@ -4,7 +4,6 @@ SFTP post-quote -FAILURE diff --git a/tests/data/test611 b/tests/data/test611 index c64d5ba94de1..3de6009e5230 100644 --- a/tests/data/test611 +++ b/tests/data/test611 @@ -23,7 +23,7 @@ sftp %PERL %SRCDIR/libtest/test610.pl mkdir %PWD/%LOGDIR/test%TESTNUMBER.dir -SFTP post-quote rename +SFTP post-quote rename a directory --key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: -Q "-rename %SFTP_PWD/%LOGDIR/test%TESTNUMBER.dir %SFTP_PWD/%LOGDIR/test%TESTNUMBER.new" sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt --insecure diff --git a/tests/data/test615 b/tests/data/test615 index 82307392ba65..8e2a6760b736 100644 --- a/tests/data/test615 +++ b/tests/data/test615 @@ -4,7 +4,6 @@ SFTP SFTP put -FAILURE diff --git a/tests/data/test620 b/tests/data/test620 index 0136b6e2bcc3..8ed5784e2d9d 100644 --- a/tests/data/test620 +++ b/tests/data/test620 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test621 b/tests/data/test621 index 33290e9953de..d15974cbd626 100644 --- a/tests/data/test621 +++ b/tests/data/test621 @@ -3,7 +3,6 @@ SCP -FAILURE diff --git a/tests/data/test622 b/tests/data/test622 index 97d0075b971d..33a73dee0315 100644 --- a/tests/data/test622 +++ b/tests/data/test622 @@ -4,7 +4,6 @@ SFTP SFTP put -FAILURE diff --git a/tests/data/test623 b/tests/data/test623 index 94f0ddc55b61..cbafe3811e51 100644 --- a/tests/data/test623 +++ b/tests/data/test623 @@ -4,7 +4,6 @@ SCP SCP upload -FAILURE diff --git a/tests/data/test626 b/tests/data/test626 index 3ded7bf87467..2ec6f5d48b14 100644 --- a/tests/data/test626 +++ b/tests/data/test626 @@ -4,7 +4,6 @@ SFTP pre-quote -FAILURE diff --git a/tests/data/test628 b/tests/data/test628 index 5394d00c6df7..c343108e2dbc 100644 --- a/tests/data/test628 +++ b/tests/data/test628 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test629 b/tests/data/test629 index 0c9e32ae946f..2869517e8f9e 100644 --- a/tests/data/test629 +++ b/tests/data/test629 @@ -3,7 +3,6 @@ SCP -FAILURE diff --git a/tests/data/test630 b/tests/data/test630 index 8d5c1f8e2971..e5e2fd8fa4d3 100644 --- a/tests/data/test630 +++ b/tests/data/test630 @@ -3,7 +3,6 @@ SFTP -FAILURE server key check diff --git a/tests/data/test631 b/tests/data/test631 index 33e7fff9b5d5..813c008e5537 100644 --- a/tests/data/test631 +++ b/tests/data/test631 @@ -3,7 +3,6 @@ SCP -FAILURE server key check diff --git a/tests/data/test632 b/tests/data/test632 index 0520bcea0bb6..1c67dff5d801 100644 --- a/tests/data/test632 +++ b/tests/data/test632 @@ -3,7 +3,6 @@ SFTP -FAILURE server key check diff --git a/tests/data/test639 b/tests/data/test639 index e87b2c931d24..529e70515a78 100644 --- a/tests/data/test639 +++ b/tests/data/test639 @@ -25,7 +25,7 @@ sftp %PERL %SRCDIR/libtest/test610.pl mkdir %PWD/%LOGDIR/test%TESTNUMBER.dir -SFTP post-quote rename * asterisk accept-fail +SFTP post-quote rename error with accept-fail --key %LOGDIR/server/curl_client_key --pubkey %LOGDIR/server/curl_client_key.pub -u %USER: -Q "-*rename %SFTP_PWD/%LOGDIR/test%TESTNUMBER-not-exists-dir %SFTP_PWD/%LOGDIR/test%TESTNUMBER.new" sftp://%HOSTIP:%SSHPORT%SFTP_PWD/%LOGDIR/file%TESTNUMBER.txt --insecure diff --git a/tests/data/test656 b/tests/data/test656 index 8f586b739d13..4e1599baccf9 100644 --- a/tests/data/test656 +++ b/tests/data/test656 @@ -3,7 +3,6 @@ SFTP -FAILURE diff --git a/tests/data/test679 b/tests/data/test679 index 478e2d25796a..6b995e3d1039 100644 --- a/tests/data/test679 +++ b/tests/data/test679 @@ -30,13 +30,13 @@ Funny-head: yesyes http -netrc with quoted password +netrc with quoted username and password --netrc-optional --netrc-file %LOGDIR/netrc%TESTNUMBER http://%HOSTIP:%HTTPPORT/ -machine %HOSTIP login user1 password "with spaces and \"\n\r\t\a" +machine %HOSTIP login "user one" password "with spaces and \"\n\r\t\a" @@ -45,7 +45,7 @@ machine %HOSTIP login user1 password "with spaces and \"\n\r\t\a" GET / HTTP/1.1 Host: %HOSTIP:%HTTPPORT -Authorization: Basic %b64[user1:with%20spaces%20and%20"%0a%0d%09a]b64% +Authorization: Basic %b64[user%20one:with%20spaces%20and%20"%0a%0d%09a]b64% User-Agent: curl/%VERSION Accept: */* diff --git a/tests/data/test688 b/tests/data/test688 index 9de1bab933dc..44f621e33c49 100644 --- a/tests/data/test688 +++ b/tests/data/test688 @@ -36,7 +36,7 @@ xattr CURL_FAKE_XATTR=1 -basic --xattr with -O +basic --xattr with (uppercase) -O --xattr -O --output-dir %LOGDIR http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test7 b/tests/data/test7 index 0f00d8009762..ccdac1927380 100644 --- a/tests/data/test7 +++ b/tests/data/test7 @@ -35,6 +35,9 @@ HTTP with cookie parser and header recording http://%HOSTIP:%HTTPPORT/we/want/%TESTNUMBER -b none -D %LOGDIR/heads%TESTNUMBER.txt + +cookies + # Verify data after the test has been "shot" diff --git a/tests/data/test702 b/tests/data/test702 index 7b899884c5c0..ea565d4be54f 100644 --- a/tests/data/test702 +++ b/tests/data/test702 @@ -6,7 +6,6 @@ HTTP SOCKS4 connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test703 b/tests/data/test703 index e3ce17e142f2..197b0da2b4ab 100644 --- a/tests/data/test703 +++ b/tests/data/test703 @@ -6,7 +6,6 @@ HTTP SOCKS5 connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test704 b/tests/data/test704 index 0decb7cddff4..5ef11ea6f098 100644 --- a/tests/data/test704 +++ b/tests/data/test704 @@ -6,7 +6,6 @@ HTTP SOCKS4 connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test705 b/tests/data/test705 index b2d1642ba9c0..c86289be6756 100644 --- a/tests/data/test705 +++ b/tests/data/test705 @@ -6,7 +6,6 @@ HTTP SOCKS5 connect to non-listen -FAILURE # Server-side diff --git a/tests/data/test708 b/tests/data/test708 index 8c52751423f7..e96900b52bd4 100644 --- a/tests/data/test708 +++ b/tests/data/test708 @@ -39,7 +39,7 @@ socks4 all_proxy=socks4://%HOSTIP:%SOCKSPORT -HTTP GET via SOCKS4 proxy +HTTP GET via SOCKS4 all_proxy http://%HOSTIP:%HTTPPORT/%TESTNUMBER diff --git a/tests/data/test741 b/tests/data/test741 index 0c4e0e1a5c9d..2c118b657d79 100644 --- a/tests/data/test741 +++ b/tests/data/test741 @@ -22,7 +22,7 @@ http HOME=%PWD/%LOGDIR -IPFS malformed gw URL from multiline gateway file, first line no url +IPFS malformed gw URL from multiline gateway file, first line no URL ipfs://bafybeidecnvkrygux6uoukouzps5ofkeevoqland7kopseiod6pzqvjg7u diff --git a/tests/data/test743 b/tests/data/test743 index cf6eb8da8d96..8627c36c107c 100644 --- a/tests/data/test743 +++ b/tests/data/test743 @@ -31,7 +31,7 @@ Funny-head: yesyes http ---config with a 127 byte line +--config with a 127-byte line -A pointless diff --git a/tests/data/test744 b/tests/data/test744 index 7db113b03675..06f99e83792c 100644 --- a/tests/data/test744 +++ b/tests/data/test744 @@ -45,7 +45,7 @@ http http-proxy ---netrc-file with a 127 byte line +--netrc-file with a 127-byte line machine foo.host login foo password baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar diff --git a/tests/data/test75 b/tests/data/test75 index 40c0d3b4dea4..b76cefbf77d9 100644 --- a/tests/data/test75 +++ b/tests/data/test75 @@ -5,7 +5,6 @@ HTTP HTTP GET globbing -FAILURE # Server-side @@ -31,7 +30,7 @@ HTTP, urlglob retrieval with bad range 3 -curl: (3) bad range in URL position 47: +curl: (3) bad range in position 47: http://a-site-never-accessed.example.org/[2-1] ^ diff --git a/tests/data/test750 b/tests/data/test750 index 8e4d220c7480..03352ece66b9 100644 --- a/tests/data/test750 +++ b/tests/data/test750 @@ -32,7 +32,7 @@ http proxy -HTTP CONNECT with proxy returning just HTML and closing +HTTP CONNECT with proxy returning HTML and closing http://test.example --proxy http://%HOSTIP:%HTTPPORT --proxytunnel -sS diff --git a/tests/data/test758 b/tests/data/test758 index df39300fe2ae..08f024fecd61 100644 --- a/tests/data/test758 +++ b/tests/data/test758 @@ -39,7 +39,7 @@ https lib%TESTNUMBER -multi_socket interface transfer with callbacks returning error +HTTPS multi_socket interface transfer with callbacks returning error https://localhost:%HTTPSPORT/file%TESTNUMBER diff --git a/tests/data/test759 b/tests/data/test759 index 9c67e30f2d94..7ad896bbccce 100644 --- a/tests/data/test759 +++ b/tests/data/test759 @@ -18,7 +18,7 @@ glob '{,' # Verify data after the test has been "shot" -# curl: (3) unmatched brace in URL position 1: +# curl: (3) unmatched brace in position 1: 3 diff --git a/tests/data/test761 b/tests/data/test761 index eec55e297fca..2772a8f78fa8 100644 --- a/tests/data/test761 +++ b/tests/data/test761 @@ -22,8 +22,8 @@ http://testingthis/%repeat[201 x {a}b]% 3 -curl: (3) too many {} sets in URL position 403: -http://testingthis/%repeat[113 x {a}b]%{a +curl: (3) too many {} sets in position 403: +http://testingthis/%repeat[114 x {a}b]%{a diff --git a/tests/data/test779 b/tests/data/test779 index 16aebce880c4..42ee36363439 100644 --- a/tests/data/test779 +++ b/tests/data/test779 @@ -6,6 +6,7 @@ HTTP IMAP oauth2-bearer followlocation +--resolve # Server-side diff --git a/tests/data/test795 b/tests/data/test795 index 859040414379..a0d168d5ffb2 100644 --- a/tests/data/test795 +++ b/tests/data/test795 @@ -5,6 +5,7 @@ HTTP IMAP followlocation +--resolve # Server-side diff --git a/tests/data/test800 b/tests/data/test800 index 512f9bbcae2a..2c4d97088efa 100644 --- a/tests/data/test800 +++ b/tests/data/test800 @@ -27,7 +27,7 @@ body imap -IMAP FETCH message +IMAP FETCH message with MAILINDEX 'imap://%HOSTIP:%IMAPPORT/%TESTNUMBER/;MAILINDEX=1' -u '"user:sec"ret{' diff --git a/tests/data/test803 b/tests/data/test803 index 20f84f83d7a8..6c4c67697cf1 100644 --- a/tests/data/test803 +++ b/tests/data/test803 @@ -6,7 +6,6 @@ IMAP Clear Text SELECT UIDVALIDITY -FAILURE diff --git a/tests/data/test830 b/tests/data/test830 index 755629bc4b6f..36ab0a31ecba 100644 --- a/tests/data/test830 +++ b/tests/data/test830 @@ -44,7 +44,7 @@ IMAP CRAM-MD5 graceful cancellation 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "LOGOUT" +# transfer and such a connection does not get a "LOGOUT" A001 CAPABILITY A002 AUTHENTICATE CRAM-MD5 diff --git a/tests/data/test831 b/tests/data/test831 index be101931d7d9..436f98fd10fc 100644 --- a/tests/data/test831 +++ b/tests/data/test831 @@ -45,7 +45,7 @@ IMAP NTLM graceful cancellation 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "LOGOUT" +# transfer and such a connection does not get a "LOGOUT" A001 CAPABILITY A002 AUTHENTICATE NTLM diff --git a/tests/data/test832 b/tests/data/test832 index 2b455901dc63..bcc74657248e 100644 --- a/tests/data/test832 +++ b/tests/data/test832 @@ -46,7 +46,7 @@ IMAP DIGEST-MD5 graceful cancellation 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "LOGOUT" +# transfer and such a connection does not get a "LOGOUT" A001 CAPABILITY A002 AUTHENTICATE DIGEST-MD5 diff --git a/tests/data/test844 b/tests/data/test844 index 6761a3aa9404..7237827d9872 100644 --- a/tests/data/test844 +++ b/tests/data/test844 @@ -39,7 +39,7 @@ IMAP OAuth 2.0 (OAUTHBEARER) failure as continuation 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "LOGOUT" +# transfer and such a connection does not get a "LOGOUT" A001 CAPABILITY A002 AUTHENTICATE OAUTHBEARER diff --git a/tests/data/test845 b/tests/data/test845 index 09068062872f..97f61b77812c 100644 --- a/tests/data/test845 +++ b/tests/data/test845 @@ -41,7 +41,7 @@ IMAP OAuth 2.0 (OAUTHBEARER) failure as continuation with initial response 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "LOGOUT" +# transfer and such a connection does not get a "LOGOUT" A001 CAPABILITY A002 AUTHENTICATE OAUTHBEARER %b64[n,a=user,%01host=127.0.0.1%01port=%IMAPPORT%01auth=Bearer mF_9.B5f-4.1JqM%01%01]b64% diff --git a/tests/data/test847 b/tests/data/test847 index e175ba106218..8ee2a22b38c3 100644 --- a/tests/data/test847 +++ b/tests/data/test847 @@ -27,7 +27,7 @@ body imap -IMAP FETCH message +IMAP FETCH message with UID 'imap://%HOSTIP:%IMAPPORT/%TESTNUMBER/;UID=1' -u '"user:sec"ret{' diff --git a/tests/data/test849 b/tests/data/test849 index c48f4447fa42..390c33132697 100644 --- a/tests/data/test849 +++ b/tests/data/test849 @@ -38,7 +38,7 @@ IMAP plain auth with alt authorization identity (Not authorized) 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "LOGOUT" +# transfer and such a connection does not get a "LOGOUT" A001 CAPABILITY A002 AUTHENTICATE PLAIN diff --git a/tests/data/test852 b/tests/data/test852 index 43779dcdc898..64e2ad33ada2 100644 --- a/tests/data/test852 +++ b/tests/data/test852 @@ -5,7 +5,6 @@ POP3 Clear Text LIST -FAILURE diff --git a/tests/data/test855 b/tests/data/test855 index 966eeaa8226a..3e01b3221670 100644 --- a/tests/data/test855 +++ b/tests/data/test855 @@ -5,7 +5,6 @@ POP3 Clear Text RETR -FAILURE diff --git a/tests/data/test856 b/tests/data/test856 index 1bd80741937c..553907a1147e 100644 --- a/tests/data/test856 +++ b/tests/data/test856 @@ -4,7 +4,6 @@ POP3 Clear Text -FAILURE @@ -35,7 +34,7 @@ pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u user:wrong 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" CAPA USER user diff --git a/tests/data/test87 b/tests/data/test87 index 620ae384886c..8477513f0ad5 100644 --- a/tests/data/test87 +++ b/tests/data/test87 @@ -6,7 +6,6 @@ HTTP HTTP GET globbing [] range -FAILURE # Server-side diff --git a/tests/data/test876 b/tests/data/test876 index ffc280281b16..3a814687291d 100644 --- a/tests/data/test876 +++ b/tests/data/test876 @@ -45,7 +45,7 @@ pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u user:secret 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" CAPA AUTH CRAM-MD5 diff --git a/tests/data/test877 b/tests/data/test877 index 8433b8118bbe..8f359ff8ec03 100644 --- a/tests/data/test877 +++ b/tests/data/test877 @@ -46,7 +46,7 @@ pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u testuser:testpass 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" CAPA AUTH NTLM diff --git a/tests/data/test878 b/tests/data/test878 index 23f074d53fa1..588a22b5026c 100644 --- a/tests/data/test878 +++ b/tests/data/test878 @@ -47,7 +47,7 @@ pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u user:secret 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" CAPA AUTH DIGEST-MD5 diff --git a/tests/data/test889 b/tests/data/test889 index bd5615bd2ba5..8a808beda5a6 100644 --- a/tests/data/test889 +++ b/tests/data/test889 @@ -42,7 +42,7 @@ pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u user --oauth2-bearer mF_9.B5f-4.1JqM 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" CAPA AUTH OAUTHBEARER diff --git a/tests/data/test890 b/tests/data/test890 index 4aec72a3848e..0892ba5b476b 100644 --- a/tests/data/test890 +++ b/tests/data/test890 @@ -42,7 +42,7 @@ pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u user --oauth2-bearer mF_9.B5f-4.1JqM --s 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" CAPA AUTH OAUTHBEARER %b64[n,a=user,%01host=127.0.0.1%01port=%POP3PORT%01auth=Bearer mF_9.B5f-4.1JqM%01%01]b64% diff --git a/tests/data/test893 b/tests/data/test893 index 1ba48a23a117..2e2ed5215276 100644 --- a/tests/data/test893 +++ b/tests/data/test893 @@ -40,7 +40,7 @@ pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u kurt:xipj3plmq --sasl-authzid ursel 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" CAPA AUTH PLAIN diff --git a/tests/data/test932 b/tests/data/test932 index e039cda6370c..ec002ae314c6 100644 --- a/tests/data/test932 +++ b/tests/data/test932 @@ -44,7 +44,7 @@ smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-fr 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" EHLO %TESTNUMBER AUTH CRAM-MD5 diff --git a/tests/data/test933 b/tests/data/test933 index 7fce345e02a8..0c4a8c42cd7e 100644 --- a/tests/data/test933 +++ b/tests/data/test933 @@ -45,7 +45,7 @@ smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-fr 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" EHLO %TESTNUMBER AUTH NTLM diff --git a/tests/data/test934 b/tests/data/test934 index 3b9ea78090d0..c440eb4974bc 100644 --- a/tests/data/test934 +++ b/tests/data/test934 @@ -46,7 +46,7 @@ smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-fr 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" EHLO %TESTNUMBER AUTH DIGEST-MD5 diff --git a/tests/data/test94 b/tests/data/test94 index bca0a8e1cc71..30c370ef9fe5 100644 --- a/tests/data/test94 +++ b/tests/data/test94 @@ -6,7 +6,6 @@ HTTPS HTTP GET HTTP CONNECT HTTP proxy -FAILURE # Server-side diff --git a/tests/data/test948 b/tests/data/test948 index 98c86a78c6ab..6955f993a90c 100644 --- a/tests/data/test948 +++ b/tests/data/test948 @@ -45,7 +45,7 @@ smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-fr 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" EHLO %TESTNUMBER AUTH OAUTHBEARER diff --git a/tests/data/test949 b/tests/data/test949 index 954664ad2961..d2407048c58b 100644 --- a/tests/data/test949 +++ b/tests/data/test949 @@ -44,7 +44,7 @@ smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-fr 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" EHLO %TESTNUMBER AUTH OAUTHBEARER %b64[n,a=user,%01host=127.0.0.1%01port=%SMTPPORT%01auth=Bearer mF_9.B5f-4.1JqM%01%01]b64% diff --git a/tests/data/test950 b/tests/data/test950 index 3ea2cb7b51a5..6d6fcc303389 100644 --- a/tests/data/test950 +++ b/tests/data/test950 @@ -24,7 +24,7 @@ smtp SMTP VRFY with custom request -# the custom request just does it lowercase to remain the same command +# the custom request does it lowercase to remain the same command smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient --request "vrfy" diff --git a/tests/data/test954 b/tests/data/test954 index 29f85fb1055d..1d39ff9f1059 100644 --- a/tests/data/test954 +++ b/tests/data/test954 @@ -42,7 +42,7 @@ smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-fr 67 # The multi interface considers a broken "CONNECT" as a prematurely broken -# transfer and such a connection will not get a "QUIT" +# transfer and such a connection does not get a "QUIT" EHLO %TESTNUMBER AUTH PLAIN diff --git a/tests/data/test962 b/tests/data/test962 index 07bb95905c70..a3fb2c99a8e6 100644 --- a/tests/data/test962 +++ b/tests/data/test962 @@ -25,7 +25,7 @@ codeset-utf8 LC_ALL=C.UTF-8 -SMTP without SMTPUTF8 support - UTF-8 based sender (host part only) +SMTP without SMTPUTF8 support - UTF-8 based sender, with IDN From: different diff --git a/tests/data/test99 b/tests/data/test99 index 6a4f27677496..4582eba4065d 100644 --- a/tests/data/test99 +++ b/tests/data/test99 @@ -6,7 +6,6 @@ HTTP HTTP GET Resume Largefile -FAILURE # Server-side diff --git a/tests/devtest.pl b/tests/devtest.pl index ed9958907445..b71689c857e5 100755 --- a/tests/devtest.pl +++ b/tests/devtest.pl @@ -69,7 +69,7 @@ BEGIN # This function is currently required to be here by servers.pm # This is copied from runtests.pl # -my $uname_release = `uname -r`; +my $uname_release = qx(uname -r); my $is_wsl = $uname_release =~ /Microsoft$/; sub logmsg { for(@_) { @@ -94,7 +94,7 @@ sub parseprotocols { # Generate a "proto-ipv6" version of each protocol to match the # IPv6 name and a "proto-unix" to match the variant which # uses Unix domain sockets. This works even if support is not - # compiled in because the test will fail. + # compiled in because the test fails. push @protocols, map(("$_-ipv6", "$_-unix"), @protocols); # 'http-proxy' is used in test cases to do CONNECT through @@ -108,7 +108,7 @@ sub parseprotocols { # Initialize @protocols from the curl binary under test # sub init_protocols { - for (`$CURL -V 2>$dev_null`) { + for (qx($CURL -V 2>$dev_null)) { if(m/^Protocols: (.*)$/) { parseprotocols($1); } diff --git a/tests/ech_combos.py b/tests/ech_combos.py index 8c100c2bb734..8b15eff41fe1 100755 --- a/tests/ech_combos.py +++ b/tests/ech_combos.py @@ -26,7 +26,7 @@ # # Python3 program to print all combination of size r in an array of size n. # This is used to generate test lines in tests/ech_test.sh. -# This will be discarded in the process of moving from experimental, +# This is discarded in the process of moving from experimental, # but is worth preserving for the moment in case of changes to the # ECH command line args @@ -65,7 +65,7 @@ def CombinationRepetitionUtil(chosen, arr, badarr, index, chosen[index] = arr[start] # Current is excluded, replace it - # with next (Note that i+1 is passed, + # with next (Note that i + 1 is passed, # but index is not changed) CombinationRepetitionUtil(chosen, arr, badarr, index + 1, r, start, end) diff --git a/tests/ech_tests.sh b/tests/ech_tests.sh index 3944793b702b..5b6f60d55558 100755 --- a/tests/ech_tests.sh +++ b/tests/ech_tests.sh @@ -31,7 +31,7 @@ # TODO: Translate this into something that approximates a valid curl test:-) # Should be useful though even before such translation and a pile less work # to do this than that. The pile of work required would include making an -# ECH-enabled server and a DoH server. For now, this is just run manually. +# ECH-enabled server and a DoH server. For now, this is run manually. # # set -x @@ -223,7 +223,7 @@ echo "-----" > "$logfile" echo "Running $0 at $NOW" >> "$logfile" echo "Running $0 at $NOW" -# check we have the binaries needed and which TLS library we will be using +# check we have the binaries needed and which TLS library we are using if [ -f "$OSSL"/libssl.so ]; then have_ossl="yes" fi @@ -253,9 +253,9 @@ fi wolf_cnt=$($CURL "${CURL_PARAMS[@]}" -V 2> /dev/null | grep -c wolfSSL) if ((wolf_cnt == 1)); then using_wolf="yes" - # for some reason curl+wolfSSL dislikes certs that are ok - # for browsers, so we will test using "insecure" mode (-k) - # but that is ok here as we are only interested in ECH testing + # for some reason curl + wolfSSL dislikes certs that are ok + # for browsers, so we test using "insecure" mode (-k) + # but that is OK here as we are only interested in ECH testing CURL_PARAMS+=(-k) fi # check if we have dig and it knows https or not @@ -474,7 +474,7 @@ done # Check various command line options, if we are good so far if [[ "$using_ossl" == "yes" && "$allgood" == "yes" ]]; then - # use this test URL as it will tell us if things worked + # use this test URL as it tells us if things worked turl="https://defo.ie/ech-check.php" echo "cli_test with $turl" echo "cli_test with $turl" >> "$logfile" @@ -498,7 +498,7 @@ if [[ "$using_ossl" == "yes" && "$allgood" == "yes" ]]; then # ecl:ecl can be correct, incorrect or missing # ech:pn can be correct, incorrect or missing # in all cases the "last" argument provided should "win" - # but only one of hard, true, grease or false will apply + # but only one of hard, true, grease or false applies turl="https://defo.ie/ech-check.php" echconfiglist=$(get_ech_configlist defo.ie) goodecl=$echconfiglist @@ -790,7 +790,7 @@ if [[ "$using_ossl" == "yes" && "$allgood" == "yes" ]]; then turl="https://tcd.ie" echo "cli_test with $turl" echo "cli_test with $turl" >> "$logfile" - # the params below do not matter much here as we will fail anyway + # the params below do not matter much here as we fail anyway echconfiglist=$(get_ech_configlist defo.ie) goodecl=$echconfiglist badecl="$goodecl" @@ -1083,14 +1083,14 @@ else echo "NOT all good, log in $logfile" fi -# send a mail to root (will be forwarded) but just once every 24 hours +# send a mail to root (it is forwarded) but only once every 24 hours # 'cause we only really need "new" news itsnews="yes" age_of_news=0 if [ -f "$LTOP"/bad_runs ]; then age_of_news=$(fileage "$LTOP"/bad_runs) # only consider news "new" if we have not mailed today - if ((age_of_news < 24*3600)); then + if ((age_of_news < 24 * 3600)); then itsnews="no" fi fi diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index 32080b21f6f6..bbfa05ce7419 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -106,7 +106,7 @@ BEGIN #********************************************************************** # global vars used for filenames # -my $PORTFILE="ftpserver.port"; # server port filename +my $PORTFILE = "ftpserver.port"; # server port filename my $portfile; # server port file path my $pidfile; # server pid filename my $mainsockf_pidfile; # pid file for primary connection sockfilt process @@ -171,7 +171,7 @@ BEGIN # $ftptargetdir is keeping the fake "name" of LIST directory. # my $ftplistparserstate; -my $ftptargetdir=""; +my $ftptargetdir = ""; #********************************************************************** # global variables used when running an FTP server to keep state info @@ -196,9 +196,9 @@ BEGIN my $POP3_TIMESTAMP = "<1972.987654321\@curl>"; #********************************************************************** -# exit_signal_handler will be triggered to indicate that the program +# exit_signal_handler is triggered to indicate that the program # should finish its execution in a controlled way as soon as possible. -# For now, program will also terminate from within this handler. +# For now, program also terminates from within this handler. # sub exit_signal_handler { my $signame = shift; @@ -215,7 +215,7 @@ sub exit_signal_handler { sub ftpmsg { # append to the server.input file - open(my $input, ">>", "$logdir/server$idstr.input") || + open(my $input, ">>", "$logdir/server$idstr.input") or logmsg "failed to open $logdir/server$idstr.input\n"; print $input @_; @@ -227,9 +227,9 @@ sub ftpmsg { } #********************************************************************** -# eXsysread is a wrapper around perl's sysread() function. This will -# repeat the call to sysread() until it has actually read the complete -# number of requested bytes or an unrecoverable condition occurs. +# eXsysread is a wrapper around perl's sysread() function. This repeats +# the call to sysread() until it has actually read the complete number +# of requested bytes or an unrecoverable condition occurs. # On success returns a positive value, the number of bytes requested. # On failure or timeout returns zero. # @@ -531,7 +531,7 @@ sub senddata { #********************************************************************** # protocolsetup initializes the 'displaytext' and 'commandfunc' hashes # for the given protocol. References to protocol command callbacks are -# stored in 'commandfunc' hash, and text which will be returned to the +# stored in 'commandfunc' hash, and text which is returned to the # client before the command callback runs is stored in 'displaytext'. # sub protocolsetup { @@ -562,8 +562,8 @@ sub protocolsetup { 'LIST' => '150 here comes a directory', 'NLST' => '150 here comes a directory', 'CWD' => '250 CWD command successful.', - 'SYST' => '215 UNIX Type: L8', # just fake something - 'QUIT' => '221 bye bye baby', # just reply something + 'SYST' => '215 UNIX Type: L8', # fake something + 'QUIT' => '221 bye bye baby', # reply something 'MKD' => '257 Created your requested directory', 'REST' => '350 Yeah yeah we set it there for you', 'DELE' => '200 OK OK OK whatever you say', @@ -712,7 +712,7 @@ sub disc_handshake { } sub close_dataconn { - my ($closed)=@_; # non-zero if already disconnected + my ($closed) = @_; # non-zero if already disconnected my $datapid = processexists($datasockf_pidfile); @@ -940,12 +940,12 @@ sub DATA_smtp { logmsg "Store test number $testno in $filename\n"; - open(my $file, ">", $filename) || + open(my $file, ">", $filename) or return 0; # failed to open output my $line; - my $ulsize=0; - my $disc=0; + my $ulsize = 0; + my $disc = 0; my $raw; while(5 == (sysread \*SFREAD, $line, 5)) { if($line eq "DATA\n") { @@ -978,7 +978,7 @@ sub DATA_smtp { } elsif($line eq "DISC\n") { # disconnect! - $disc=1; + $disc = 1; printf SFWRITE "ACKD\n"; last; } @@ -1143,7 +1143,7 @@ sub QUIT_smtp { my $selected; # Any IMAP parameter can come in escaped and in double quotes. -# This function is dumb (so far) and just removes the quotes if present. +# This function is dumb (so far) and removes the quotes if present. sub fix_imap_params { foreach (@_) { $_ = $1 if /^"(.*)"$/; @@ -1290,7 +1290,7 @@ sub APPEND_imap { logmsg "Store test number $testno in $filename\n"; - open(my $file, ">", $filename) || + open(my $file, ">", $filename) or return 0; # failed to open output my $received = 0; @@ -2018,7 +2018,7 @@ sub QUIT_pop3 { ################ ################ FTP commands ################ -my $rest=0; +my $rest = 0; sub REST_ftp { $rest = $_[0]; logmsg "Set REST position to $rest\n" @@ -2135,7 +2135,7 @@ sub LIST_ftp { } sub NLST_ftp { - my @ftpdir=("file", "with space", "fake", "..", " ..", "funny", "README"); + my @ftpdir = ("file", "with space", "fake", "..", " ..", "funny", "README"); if($datasockf_conn eq 'no') { if($nodataconn425) { @@ -2242,7 +2242,7 @@ sub SIZE_ftp { } } else { - $size=0; + $size = 0; @data = getpart("reply", "data$testpart"); for(@data) { $size += length($_); @@ -2320,7 +2320,7 @@ sub RETR_ftp { my @data = getpart("reply", "data$testpart"); - my $size=0; + my $size = 0; for(@data) { $size += length($_); } @@ -2344,7 +2344,7 @@ sub RETR_ftp { senddata $send; } close_dataconn(0); - $retrweirdo=0; # switch off the weirdo again! + $retrweirdo = 0; # switch off the weirdo again! } else { my $sz = "($size bytes)"; @@ -2372,7 +2372,7 @@ sub RETR_ftp { } sub STOR_ftp { - my $testno=$_[0]; + my $testno = $_[0]; my $filename = "$logdir/upload.$testno"; @@ -2399,12 +2399,12 @@ sub STOR_ftp { sendcontrol "125 Gimme gimme gimme!\r\n"; - open(my $file, ">", $filename) || + open(my $file, ">", $filename) or return 0; # failed to open output my $line; - my $ulsize=0; - my $disc=0; + my $ulsize = 0; + my $disc = 0; while(5 == (sysread DREAD, $line, 5)) { if($line eq "DATA\n") { my $i; @@ -2425,7 +2425,7 @@ sub STOR_ftp { } elsif($line eq "DISC\n") { # disconnect! - $disc=1; + $disc = 1; printf DWRITE "ACKD\n"; last; } @@ -2454,7 +2454,7 @@ sub STOR_ftp { } sub PASV_ftp { - my ($arg, $cmd)=@_; + my ($arg, $cmd) = @_; my $pasvport; # kill previous data connection sockfilt when alive @@ -2561,13 +2561,13 @@ sub PASV_ftp { if($cmd ne "EPSV") { # PASV reply - my $p=$listenaddr; + my $p = $listenaddr; $p =~ s/\./,/g; if($pasvbadip) { - $p="1,2,3,4"; + $p = "1,2,3,4"; } sendcontrol sprintf("227 Entering Passive Mode ($p,%d,%d)\r\n", - int($pasvport/256), int($pasvport%256)); + int($pasvport / 256), int($pasvport % 256)); } else { # EPSV reply @@ -2826,23 +2826,23 @@ sub customize { %customcount = (); # %delayreply = (); # - open(my $custom, "<", "$logdir/$SERVERCMD") || + open(my $custom, "<", "$logdir/$SERVERCMD") or return 1; logmsg "FTPD: Getting commands from $logdir/$SERVERCMD\n"; while(<$custom>) { if($_ =~ /REPLY \"([A-Z]+ [A-Za-z0-9+-\/=\*. ]+)\" (.*)/) { - $fulltextreply{$1}=eval "qq{$2}"; + $fulltextreply{$1} = eval "qq{$2}"; logmsg "FTPD: set custom reply for $1\n"; } elsif($_ =~ /REPLY(LF|) ([A-Za-z0-9+\/=\*]*) (.*)/) { - $commandreply{$2}=eval "qq{$3}"; + $commandreply{$2} = eval "qq{$3}"; if($1 ne "LF") { - $commandreply{$2}.="\r\n"; + $commandreply{$2} .= "\r\n"; } else { - $commandreply{$2}.="\n"; + $commandreply{$2} .= "\n"; } if($2 eq "") { logmsg "FTPD: set custom reply for empty command\n"; @@ -2854,11 +2854,11 @@ sub customize { elsif($_ =~ /COUNT ([A-Z]+) (.*)/) { # we blank the custom reply for this command when having # been used this number of times - $customcount{$1}=$2; + $customcount{$1} = $2; logmsg "FTPD: blank custom reply for $1 command after $2 uses\n"; } elsif($_ =~ /DELAY ([A-Z]+) (\d*)/) { - $delayreply{$1}=$2; + $delayreply{$1} = $2; logmsg "FTPD: delay reply for $1 with $2 seconds\n"; } elsif($_ =~ /POSTFETCH (.*)/) { @@ -2866,22 +2866,22 @@ sub customize { $postfetch = $1; } elsif($_ =~ /SLOWDOWNDATA/) { - $ctrldelay=0; - $datadelay=0.005; + $ctrldelay = 0; + $datadelay = 0.005; logmsg "FTPD: send response data with 5ms delay per byte\n"; } elsif($_ =~ /SLOWDOWN/) { - $ctrldelay=0.005; - $datadelay=0.005; + $ctrldelay = 0.005; + $datadelay = 0.005; logmsg "FTPD: send response with 5ms delay between each byte\n"; } elsif($_ =~ /RETRWEIRDO/) { logmsg "FTPD: instructed to use RETRWEIRDO\n"; - $retrweirdo=1; + $retrweirdo = 1; } elsif($_ =~ /RETRNOSIZE/) { logmsg "FTPD: instructed to use RETRNOSIZE\n"; - $retrnosize=1; + $retrnosize = 1; } elsif($_ =~ /RETRSIZE (\d+)/) { $retrsize= $1; @@ -2889,33 +2889,33 @@ sub customize { } elsif($_ =~ /PASVBADIP/) { logmsg "FTPD: instructed to use PASVBADIP\n"; - $pasvbadip=1; + $pasvbadip = 1; } elsif($_ =~ /NODATACONN425/) { # applies to both active and passive FTP modes logmsg "FTPD: instructed to use NODATACONN425\n"; - $nodataconn425=1; - $nodataconn=1; + $nodataconn425 = 1; + $nodataconn = 1; } elsif($_ =~ /NODATACONN421/) { # applies to both active and passive FTP modes logmsg "FTPD: instructed to use NODATACONN421\n"; - $nodataconn421=1; - $nodataconn=1; + $nodataconn421 = 1; + $nodataconn = 1; } elsif($_ =~ /NODATACONN150/) { # applies to both active and passive FTP modes logmsg "FTPD: instructed to use NODATACONN150\n"; - $nodataconn150=1; - $nodataconn=1; + $nodataconn150 = 1; + $nodataconn = 1; } elsif($_ =~ /NODATACONN/) { # applies to both active and passive FTP modes logmsg "FTPD: instructed to use NODATACONN\n"; - $nodataconn=1; + $nodataconn = 1; } elsif($_ =~ /^STOR (.*)/) { - $storeresp=$1; + $storeresp = $1; logmsg "FTPD: instructed to use respond to STOR with '$storeresp'\n"; } elsif($_ =~ /CAPA (.*)/) { @@ -3150,7 +3150,7 @@ sub customize { } else { # clear it after use - $commandreply{"welcome"}=""; + $commandreply{"welcome"} = ""; if($welcome !~ /\r\n\z/) { $welcome .= "\r\n"; } @@ -3217,21 +3217,21 @@ sub customize { # IMAP is different with its identifier first on the command line if(($full =~ /^([^ ]+) ([^ ]+) (.*)/) || ($full =~ /^([^ ]+) ([^ ]+)/)) { - $cmdid=$1; # set the global variable - $FTPCMD=$2; - $FTPARG=$3; + $cmdid = $1; # set the global variable + $FTPCMD = $2; + $FTPARG = $3; } # IMAP authentication cancellation elsif($full =~ /^\*$/) { # Command id has already been set - $FTPCMD="*"; - $FTPARG=""; + $FTPCMD = "*"; + $FTPARG = ""; } # IMAP long "commands" are base64 authentication data elsif($full =~ /^[A-Z0-9+\/]*={0,2}$/i) { # Command id has already been set - $FTPCMD=$full; - $FTPARG=""; + $FTPCMD = $full; + $FTPARG = ""; } else { sendcontrol "$full BAD Command\r\n"; @@ -3239,19 +3239,19 @@ sub customize { } } elsif($full =~ /^([A-Z]{3,4})(\s(.*))?$/i) { - $FTPCMD=$1; - $FTPARG=$3; + $FTPCMD = $1; + $FTPARG = $3; } elsif($proto eq "pop3") { # POP3 authentication cancellation if($full =~ /^\*$/) { - $FTPCMD="*"; - $FTPARG=""; + $FTPCMD = "*"; + $FTPARG = ""; } # POP3 long "commands" are base64 authentication data elsif($full =~ /^[A-Z0-9+\/]*={0,2}$/i) { - $FTPCMD=$full; - $FTPARG=""; + $FTPCMD = $full; + $FTPARG = ""; } else { sendcontrol "-ERR Unrecognized command\r\n"; @@ -3261,13 +3261,13 @@ sub customize { elsif($proto eq "smtp") { # SMTP authentication cancellation if($full =~ /^\*$/) { - $FTPCMD="*"; - $FTPARG=""; + $FTPCMD = "*"; + $FTPARG = ""; } # SMTP long "commands" are base64 authentication data elsif($full =~ /^[A-Z0-9+\/]{0,512}={0,2}$/i) { - $FTPCMD=$full; - $FTPARG=""; + $FTPCMD = $full; + $FTPARG = ""; } else { sendcontrol "500 Unrecognized command\r\n"; @@ -3289,7 +3289,7 @@ sub customize { my $delay = $delayreply{$FTPCMD}; if($delay) { - # just go sleep this many seconds! + # go sleep this many seconds! logmsg("Sleep for $delay seconds\n"); my $twentieths = $delay * 20; while($twentieths--) { @@ -3312,7 +3312,7 @@ sub customize { if($text && ($text ne "")) { if($customcount{$FTPCMD} && (!--$customcount{$FTPCMD})) { # used enough times so blank the custom command reply - $commandreply{$FTPCMD}=""; + $commandreply{$FTPCMD} = ""; } sendcontrol $text; diff --git a/tests/getpart.pm b/tests/getpart.pm index 5fa4b1e62b34..fc98da210c0b 100644 --- a/tests/getpart.pm +++ b/tests/getpart.pm @@ -49,12 +49,12 @@ use Memoize; my @xml; # test data file contents my $xmlfile; # test data filename -my $warning=0; -my $trace=0; +my $warning = 0; +my $trace = 0; # Normalize the part function arguments for proper caching. This includes the # filename in the arguments since that is an implied parameter that affects the -# return value. Any error messages will only be displayed the first time, but +# return value. Any error messages are only displayed the first time, but # those are disabled by default anyway, so should never been seen outside # development. sub normalize_part { @@ -66,12 +66,12 @@ sub testcaseattr { my %hash; for(@xml) { if(($_ =~ /^ *\]*)/)) { - my $attr=$1; + my $attr = $1; while($attr =~ s/ *([^=]*)= *(\"([^\"]*)\"|\'([^\']*)\')//) { - my ($var, $cont)=($1, $2); + my ($var, $cont) = ($1, $2); $cont =~ s/^\"(.*)\"$/$1/; $cont =~ s/^\'(.*)\'$/$1/; - $hash{$var}=$cont; + $hash{$var} = $cont; } } } @@ -82,10 +82,10 @@ sub getpartattr { # if $part is undefined (ie only one argument) then # return the attributes of the section - my ($section, $part)=@_; + my ($section, $part) = @_; my %hash; - my $inside=0; + my $inside = 0; # print "Section: $section, part: $part\n"; @@ -98,13 +98,13 @@ sub getpartattr { !(defined($part))) ) { $inside++; - my $attr=$1; + my $attr = $1; while($attr =~ s/ *([^=]*)= *(\"([^\"]*)\"|\'([^\']*)\')//) { - my ($var, $cont)=($1, $2); + my ($var, $cont) = ($1, $2); $cont =~ s/^\"(.*)\"$/$1/; $cont =~ s/^\'(.*)\'$/$1/; - $hash{$var}=$cont; + $hash{$var} = $cont; } last; } @@ -121,10 +121,10 @@ sub getpartattr { memoize('getpartattr', NORMALIZER => 'normalize_part'); # cache each result sub getpart { - my ($section, $part)=@_; + my ($section, $part) = @_; my @this; - my $inside=0; + my $inside = 0; my $line; for(@xml) { @@ -176,7 +176,7 @@ sub getpart { memoize('getpart', NORMALIZER => 'normalize_part'); # cache each result sub partexists { - my ($section, $part)=@_; + my ($section, $part) = @_; my $inside = 0; @@ -195,11 +195,11 @@ sub partexists { } # The code currently never calls this more than once per part per file, so -# caching a result that will never be used again just slows things down. +# caching a result that is never used again only slows things down. # memoize('partexists', NORMALIZER => 'normalize_part'); # cache each result sub loadtest { - my ($file, $original)=@_; + my ($file, $original) = @_; if(defined $xmlfile && $file eq $xmlfile) { # This test is already loaded @@ -288,7 +288,7 @@ sub checktest { # write the test to the given file sub savetest { - my ($file)=@_; + my ($file) = @_; if(open(my $xmlh, ">", $file)) { binmode $xmlh; # for crapage systems, use binary @@ -328,7 +328,7 @@ sub striparray { # pass array *REFERENCES* ! # sub compareparts { - my ($firstref, $secondref)=@_; + my ($firstref, $secondref) = @_; # we cannot compare arrays index per index since with data chunks, # they may not be "evenly" distributed @@ -384,21 +384,21 @@ sub compareparts { # Write a given array to the specified file # sub writearray { - my ($filename, $arrayref)=@_; + my ($filename, $arrayref) = @_; - open(my $temp, ">", $filename) || die "Failure writing file"; + open(my $temp, ">", $filename) or die "Failure writing file"; binmode($temp,":raw"); # Cygwin fix for(@$arrayref) { print $temp $_; } - close($temp) || die "Failure writing file"; + close($temp) or die "Failure writing file"; } # # Load a specified file and return it as an array # sub loadarray { - my ($filename)=@_; + my ($filename) = @_; my @array; if(open(my $temp, "<", $filename)) { diff --git a/tests/globalconfig.pm b/tests/globalconfig.pm index d99d4306cf2b..9b3d1bf76c76 100644 --- a/tests/globalconfig.pm +++ b/tests/globalconfig.pm @@ -90,54 +90,54 @@ use File::Spec; # # config variables overridden by command-line options -our $verbose; # 1 to show verbose test output -our $torture; # 1 to enable torture testing -our $proxy_address; # external HTTP proxy address -our $listonly; # only list the tests -our $buildinfo; # dump buildinfo.txt -our $run_duphandle; # run curl with --test-duphandle to verify handle duplication -our $run_event_based; # run curl with --test-event to test the event API -our $automakestyle; # use automake-like test status output format -our $anyway; # continue anyway, even if a test fail -our $CURLVERSION=""; # curl's reported version number -our $CURLVERNUM=""; # curl's reported version number (without -DEV) -our $randseed = 0; # random number seed -our $maxtime; # curl command timeout override -our $mintotal; # minimum number of tests to run +our $verbose; # 1 to show verbose test output +our $torture; # 1 to enable torture testing +our $proxy_address; # external HTTP proxy address +our $listonly; # only list the tests +our $buildinfo; # dump buildinfo.txt +our $run_duphandle; # run curl with --test-duphandle to verify handle duplication +our $run_event_based; # run curl with --test-event to test the event API +our $automakestyle; # use automake-like test status output format +our $anyway; # continue anyway, even if a test fail +our $CURLVERSION = ""; # curl's reported version number +our $CURLVERNUM = ""; # curl's reported version number (without -DEV) +our $randseed = 0; # random number seed +our $maxtime; # curl command timeout override +our $mintotal; # minimum number of tests to run # paths our $pwd = getcwd(); # current working directory our $srcdir = $ENV{'srcdir'} || '.'; # root of the test source code -our $perlcmd=shell_quote($^X); -our $perl="$perlcmd -I. " . shell_quote("-I$srcdir"); # invoke perl like this -our $LOGDIR="log"; # root of the log directory; this will be different for - # each runner in multiprocess mode -our $LIBDIR=dirsepadd("./libtest/" . ($ENV{'CURL_DIRSUFFIX'} || '')); -our $UNITDIR=dirsepadd("./unit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); -our $TUNITDIR=dirsepadd("./tunit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); -our $SRVDIR=dirsepadd("./server/" . ($ENV{'CURL_DIRSUFFIX'} || '')); -our $TESTDIR="$srcdir/data"; -our $CURL=dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . +our $perlcmd = shell_quote($^X); +our $perl = "$perlcmd -I. " . shell_quote("-I$srcdir"); # invoke perl like this +our $LOGDIR = "log"; # root of the log directory; this is different for + # each runner in multiprocess mode +our $LIBDIR = dirsepadd("./libtest/" . ($ENV{'CURL_DIRSUFFIX'} || '')); +our $UNITDIR = dirsepadd("./unit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); +our $TUNITDIR = dirsepadd("./tunit/" . ($ENV{'CURL_DIRSUFFIX'} || '')); +our $SRVDIR = dirsepadd("./server/" . ($ENV{'CURL_DIRSUFFIX'} || '')); +our $TESTDIR = "$srcdir/data"; +our $CURL = dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . "curl".exe_ext('TOOL'); # what curl binary to run on the tests -our $CURLINFO=dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . +our $CURLINFO = dirsepadd("../src/" . ($ENV{'CURL_DIRSUFFIX'} || '')) . "curlinfo".exe_ext('TOOL'); # what curlinfo binary to run on the tests -our $VCURL=$CURL; # what curl binary to use to verify the servers with - # VCURL is handy to set to the system one when the one you - # just built hangs or crashes and thus prevent verification +our $VCURL = $CURL; # what curl binary to use to verify the servers with + # VCURL is handy to set to the system one when the one you + # built hangs or crashes and thus prevent verification # the path to the script that analyzes the memory debug output file -our $memanalyze="$perl " . shell_quote("$srcdir/memanalyze.pl"); +our $memanalyze = "$perl " . shell_quote("$srcdir/memanalyze.pl"); our $valgrind; # path to valgrind, or empty if disabled our $dev_null = File::Spec->devnull(); # null device path, eg: /dev/null # paths in $LOGDIR -our $LOCKDIR = "lock"; # root of the server directory with lock files -our $PIDDIR = "server"; # root of the server directory with PID files -our $SERVERIN="server.input"; # what curl sent the server -our $PROXYIN="proxy.input"; # what curl sent the proxy -our $MEMDUMP="memdump"; # file that the memory debugging creates -our $SERVERCMD="server.cmd"; # copy server instructions here -our $DNSCMD="dnsd.cmd"; # write DNS instructions here +our $LOCKDIR = "lock"; # root of the server directory with lock files +our $PIDDIR = "server"; # root of the server directory with PID files +our $SERVERIN = "server.input"; # what curl sent the server +our $PROXYIN = "proxy.input"; # what curl sent the proxy +our $MEMDUMP = "memdump"; # file that the memory debugging creates +our $SERVERCMD = "server.cmd"; # copy server instructions here +our $DNSCMD = "dnsd.cmd"; # write DNS instructions here # other config variables our @protocols; # array of lowercase supported protocol servers diff --git a/tests/http-server.pl b/tests/http-server.pl index 006b6f381781..14c66a8b42a6 100755 --- a/tests/http-server.pl +++ b/tests/http-server.pl @@ -40,7 +40,7 @@ BEGIN ); my $verbose = 0; # set to 1 for debugging -my $port = 8990; # just a default +my $port = 8990; # a default my $unix_socket; # location to place a listening Unix socket my $ipvnum = 4; # default IP version of http server my $idnum = 1; # default http server instance number diff --git a/tests/http/CMakeLists.txt b/tests/http/CMakeLists.txt index 3373f8af2b39..9801d51907c5 100644 --- a/tests/http/CMakeLists.txt +++ b/tests/http/CMakeLists.txt @@ -28,6 +28,12 @@ if(NOT CADDY) endif() mark_as_advanced(CADDY) +find_program(H2O "h2o") # /usr/local/bin/h2o +if(NOT H2O) + set(H2O "") +endif() +mark_as_advanced(H2O) + find_program(VSFTPD "vsftpd") # /usr/sbin/vsftpd if(NOT VSFTPD) set(VSFTPD "") diff --git a/tests/http/Makefile.am b/tests/http/Makefile.am index f4dc92f61b06..7232c1e8aa15 100644 --- a/tests/http/Makefile.am +++ b/tests/http/Makefile.am @@ -31,6 +31,7 @@ TESTENV = \ testenv/dnsd.py \ testenv/dante.py \ testenv/env.py \ + testenv/h2o.py \ testenv/httpd.py \ testenv/mod_curltest/mod_curltest.c \ testenv/nghttpx.py \ @@ -72,6 +73,7 @@ EXTRA_DIST = \ test_40_socks.py \ test_50_scp.py \ test_51_sftp.py \ + test_60_h3_proxy.py \ $(TESTENV) clean-local: diff --git a/tests/http/config.ini.in b/tests/http/config.ini.in index 78808e966db9..daf9869b7cda 100644 --- a/tests/http/config.ini.in +++ b/tests/http/config.ini.in @@ -44,3 +44,6 @@ danted = @DANTED@ [sshd] sshd = @SSHD@ sftpd = @SFTPD@ + +[h2o] +h2o = @H2O@ diff --git a/tests/http/conftest.py b/tests/http/conftest.py index 08da73ac0fdf..225d63fe6d19 100644 --- a/tests/http/conftest.py +++ b/tests/http/conftest.py @@ -1,4 +1,4 @@ -#*************************************************************************** +# *************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | # / __| | | | |_) | | @@ -31,9 +31,10 @@ import pytest from testenv.env import EnvConfig -sys.path.append(os.path.join(os.path.dirname(__file__), '.')) +sys.path.append(os.path.join(os.path.dirname(__file__), ".")) from testenv import Env, Httpd, Nghttpx, NghttpxFwd, NghttpxQuic, Sshd +from testenv.h2o import H2oProxy, H2oServer log = logging.getLogger(__name__) @@ -42,51 +43,47 @@ def pytest_report_header(config): # Env inits its base properties only once, we can report them here env = Env() report = [ - f'Testing curl {env.curl_version()}', - f' platform: {platform.platform()}', - f' curl: Version: {env.curl_version_string()}', - f' curl: Features: {env.curl_features_string()}', - f' curl: Protocols: {env.curl_protocols_string()}', - f' httpd: {env.httpd_version()}', - f' httpd-proxy: {env.httpd_version()}' + f"Testing curl {env.curl_version()}", + f" platform: {platform.platform()}", + f" curl: Version: {env.curl_version_string()}", + f" curl: Features: {env.curl_features_string()}", + f" curl: Protocols: {env.curl_protocols_string()}", + f" httpd: {env.httpd_version()}", + f" httpd-proxy: {env.httpd_version()}", ] if env.have_h3(): - report.extend([ - f' nghttpx: {env.nghttpx_version()}' - ]) + report.extend([f" nghttpx: {env.nghttpx_version()}"]) + if env.have_h2o(): + report.extend([f" h2o: {env.h2o_version()}"]) if env.has_caddy(): - report.extend([ - f' Caddy: {env.caddy_version()}' - ]) + report.extend([f" Caddy: {env.caddy_version()}"]) if env.has_vsftpd(): - report.extend([ - f' VsFTPD: {env.vsftpd_version()}' - ]) - buildinfo_fn = os.path.join(env.build_dir, 'buildinfo.txt') + report.extend([f" VsFTPD: {env.vsftpd_version()}"]) + buildinfo_fn = os.path.join(env.build_dir, "buildinfo.txt") if os.path.exists(buildinfo_fn): - with open(buildinfo_fn, 'r') as file_in: + with open(buildinfo_fn, "r") as file_in: for line in file_in: line = line.strip() - if line and not line.startswith('#'): + if line and not line.startswith("#"): report.extend([line]) - return '\n'.join(report) + return "\n".join(report) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def env_config(pytestconfig, testrun_uid, worker_id) -> EnvConfig: - return EnvConfig(pytestconfig=pytestconfig, - testrun_uid=testrun_uid, - worker_id=worker_id) + return EnvConfig( + pytestconfig=pytestconfig, testrun_uid=testrun_uid, worker_id=worker_id + ) -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope="session", autouse=True) def env(pytestconfig, env_config) -> Env: env = Env(pytestconfig=pytestconfig, env_config=env_config) level = logging.DEBUG if env.verbose > 0 else logging.INFO - logging.getLogger('').setLevel(level=level) - if not env.curl_has_protocol('http'): + logging.getLogger("").setLevel(level=level) + if not env.curl_has_protocol("http"): pytest.skip("curl built without HTTP support") - if not env.curl_has_protocol('https'): + if not env.curl_has_protocol("https"): pytest.skip("curl built without HTTPS support") if env.setup_incomplete(): pytest.skip(env.incomplete_reason()) @@ -95,23 +92,23 @@ def env(pytestconfig, env_config) -> Env: return env -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def httpd(env) -> Generator[Httpd, None, None]: httpd = Httpd(env=env) if not httpd.exists(): - pytest.skip(f'httpd not found: {env.httpd}') + pytest.skip(f"httpd not found: {env.httpd}") httpd.clear_logs() assert httpd.initial_start() yield httpd httpd.stop() -@pytest.fixture(scope='session') -def nghttpx(env, httpd) -> Generator[Union[Nghttpx,bool], None, None]: +@pytest.fixture(scope="session") +def nghttpx(env, httpd) -> Generator[Union[Nghttpx, bool], None, None]: nghttpx = NghttpxQuic(env=env) if nghttpx.exists(): if not nghttpx.supports_h3() and env.have_h3_curl(): - log.warning('nghttpx does not support QUIC, but curl does') + log.warning("nghttpx does not support QUIC, but curl does") nghttpx.clear_logs() assert nghttpx.initial_start() yield nghttpx @@ -120,8 +117,8 @@ def nghttpx(env, httpd) -> Generator[Union[Nghttpx,bool], None, None]: yield False -@pytest.fixture(scope='session') -def nghttpx_fwd(env, httpd) -> Generator[Union[Nghttpx,bool], None, None]: +@pytest.fixture(scope="session") +def nghttpx_fwd(env, httpd) -> Generator[Union[Nghttpx, bool], None, None]: nghttpx = NghttpxFwd(env=env) if nghttpx.exists(): nghttpx.clear_logs() @@ -132,37 +129,67 @@ def nghttpx_fwd(env, httpd) -> Generator[Union[Nghttpx,bool], None, None]: yield False -@pytest.fixture(scope='session') -def sshd(env: Env) -> Generator[Union[Sshd,bool], None, None]: +@pytest.fixture(scope="session") +def sshd(env: Env) -> Generator[Union[Sshd, bool], None, None]: if env.has_sshd(): sshd = Sshd(env=env) - assert sshd.initial_start(), f'{sshd.dump_log()}' + assert sshd.initial_start(), f"{sshd.dump_log()}" yield sshd sshd.stop() else: yield False -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def configures_httpd(env, httpd) -> Generator[bool, None, None]: # include this fixture as test parameter if the test configures httpd itself yield True -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def configures_nghttpx(env, httpd) -> Generator[bool, None, None]: # include this fixture as test parameter if the test configures nghttpx itself yield True -@pytest.fixture(autouse=True, scope='function') +@pytest.fixture(autouse=True, scope="function") def server_reset(request, env, httpd, nghttpx): # make sure httpd is in default configuration when a test starts - if 'configures_httpd' not in request.node._fixtureinfo.argnames: + if "configures_httpd" not in request.node._fixtureinfo.argnames: httpd.reset_config() httpd.reload_if_config_changed() - if env.have_h3() and \ - 'nghttpx' in request.node._fixtureinfo.argnames and \ - 'configures_nghttpx' not in request.node._fixtureinfo.argnames: + if ( + env.have_h3() + and "nghttpx" in request.node._fixtureinfo.argnames + and "configures_nghttpx" not in request.node._fixtureinfo.argnames + ): nghttpx.reset_config() nghttpx.reload_if_config_changed() + + +@pytest.fixture(scope="session") +def h2o_server(env) -> Generator[Union[H2oServer, bool], None, None]: + h2o = H2oServer(env=env) + if env.have_h2o(): + h2o.clear_logs() + if not h2o.initial_start(): + h2o_logs = "\n".join(h2o.dump_logs()) + pytest.skip(f"h2o server failed to start\n{h2o_logs}") + yield h2o + h2o.kill() + else: + yield False + + +@pytest.fixture(scope="session") +def h2o_proxy(env) -> Generator[Union[H2oProxy, bool], None, None]: + h2o = H2oProxy(env=env) + if env.have_h2o(): + h2o.clear_logs() + if not h2o.initial_start(): + h2o_logs = "\n".join(h2o.dump_logs()) + pytest.skip(f"h2o proxy failed to start\n{h2o_logs}") + yield h2o + h2o.kill() + else: + yield False diff --git a/tests/http/requirements.txt b/tests/http/requirements.txt index feb0fedf81a2..a31374e06446 100644 --- a/tests/http/requirements.txt +++ b/tests/http/requirements.txt @@ -2,8 +2,8 @@ # # SPDX-License-Identifier: curl -cryptography==46.0.7 -filelock==3.25.2 +cryptography==48.0.1 +filelock==3.29.0 psutil==7.2.2 pytest==9.0.3 pytest-xdist==3.8.0 diff --git a/tests/http/scorecard.py b/tests/http/scorecard.py index 205b556f61e2..36947592e28c 100644 --- a/tests/http/scorecard.py +++ b/tests/http/scorecard.py @@ -55,14 +55,14 @@ class ScoreCardError(Exception): class Card: @classmethod def fmt_ms(cls, tval): - return f'{int(tval*1000)} ms' if tval >= 0 else '--' + return f'{int(tval * 1000)} ms' if tval >= 0 else '--' @classmethod def fmt_size(cls, val): - if val >= (1024*1024*1024): - return f'{val / (1024*1024*1024):0.000f}GB' + if val >= (1024 * 1024 * 1024): + return f'{val / (1024 * 1024 * 1024):0.000f}GB' if val >= (1024 * 1024): - return f'{val / (1024*1024):0.000f}MB' + return f'{val / (1024 * 1024):0.000f}MB' if val >= 1024: return f'{val / 1024:0.000f}KB' return f'{val:0.000f}B' @@ -71,8 +71,8 @@ def fmt_size(cls, val): def fmt_mbs(cls, val): if val is None or val < 0: return '--' - if val >= (1024*1024): - return f'{val/(1024*1024):.3g} MB/s' + if val >= (1024 * 1024): + return f'{val / (1024 * 1024):.3g} MB/s' if val >= 1024: return f'{val / 1024:.3g} KB/s' return f'{val:.3g} B/s' @@ -81,10 +81,10 @@ def fmt_mbs(cls, val): def fmt_speed(cls, val): if val is None or val < 0: return '--' - if val >= (10*1024*1024): - return f'{(val/(1024*1024)):.3f} MB/s' - if val >= (10*1024): - return f'{val/1024:.3f} KB/s' + if val >= (10 * 1024 * 1024): + return f'{(val / (1024 * 1024)):.3f} MB/s' + if val >= (10 * 1024): + return f'{val / 1024:.3f} KB/s' return f'{val:.3f} B/s' @classmethod @@ -92,10 +92,10 @@ def fmt_speed_result(cls, val, limit): if val is None or val < 0: return '--' pct = ((val / limit) * 100) - 100 - if val >= (10*1024*1024): - return f'{(val/(1024*1024)):.3f} MB/s, {pct:+.1f}%' - if val >= (10*1024): - return f'{val/1024:.3f} KB/s, {pct:+.1f}%' + if val >= (10 * 1024 * 1024): + return f'{(val / (1024 * 1024)):.3f} MB/s, {pct:+.1f}%' + if val >= (10 * 1024): + return f'{val / 1024:.3f} KB/s, {pct:+.1f}%' return f'{val:.3f} B/s, {pct:+.1f}%' @classmethod @@ -240,6 +240,7 @@ def __init__(self, env: Env, with_flame: bool = False, socks_args: Optional[List[str]] = None, limit_rate: Optional[str] = None, + http_plain: bool = False, suppress_cl: bool = False): self.verbose = verbose self.env = env @@ -254,15 +255,17 @@ def __init__(self, env: Env, self._socks_args = socks_args self._limit_rate_num = 0 self._limit_rate = limit_rate + self._http_plain = http_plain + self._scheme = 'http' if http_plain else 'https' if self._limit_rate: m = re.match(r'(\d+(\.\d+)?)([gmkb])?', self._limit_rate.lower()) if not m: raise Exception(f'unrecognised limit-rate: {self._limit_rate}') self._limit_rate_num = float(m.group(1)) if m.group(3) == 'g': - self._limit_rate_num *= 1024*1024*1024 + self._limit_rate_num *= 1024 * 1024 * 1024 elif m.group(3) == 'm': - self._limit_rate_num *= 1024*1024 + self._limit_rate_num *= 1024 * 1024 elif m.group(3) == 'k': self._limit_rate_num *= 1024 elif m.group(3) == 'b': @@ -300,7 +303,7 @@ def handshakes(self) -> Dict[str, Any]: curl = self.mk_curl_client() args = [ '--http3-only' if self.protocol == 'h3' else '--http2', - f'--{ipv}', f'https://{authority}/' + f'--{ipv}', f'{self._scheme}://{authority}/' ] r = curl.run_direct(args=args, with_stats=True) if r.exit_code == 0 and len(r.stats) == 1: @@ -335,7 +338,7 @@ def setup_resources(self, server_docs: str, self._make_docs_file(docs_dir=server_docs, fname=fname, fsize=fsize) self._make_docs_file(docs_dir=server_docs, - fname='reqs10.data', fsize=10*1024) + fname='reqs10.data', fsize=10 * 1024) def _check_downloads(self, r: ExecResult, count: int): error = '' @@ -456,7 +459,7 @@ def downloads(self, count: int, fsizes: List[int], meta: Dict[str, Any]) -> Dict 'sval': Card.fmt_size(fsize) }] self.info(f'{row[0]["sval"]} downloads...') - url = f'https://{self.env.domain1}:{self.server_port}/score{row[0]["sval"]}.data' + url = f'{self._scheme}://{self.env.domain1}:{self.server_port}/score{row[0]["sval"]}.data' if 'single' in cols: row.append(self.dl_single(url=url, nsamples=nsamples)) if count > 1: @@ -578,7 +581,7 @@ def uploads(self, count: int, fsizes: List[int], meta: Dict[str, Any]) -> Dict[s 'sval': Card.fmt_size(fsize) }] self.info(f'{row[0]["sval"]} uploads...') - url = f'https://{self.env.domain1}:{self.server_port}/curltest/put' + url = f'{self._scheme}://{self.env.domain1}:{self.server_port}/curltest/put' fname = f'upload{row[0]["sval"]}.data' fpath = self._make_docs_file(docs_dir=self.env.gen_dir, fname=fname, fsize=fsize) @@ -635,8 +638,8 @@ def do_requests(self, url: str, count: int, max_parallel: int = 1, nsamples: int return Card.mk_reqs_cell(samples, profiles, errors) def requests(self, count: int, meta: Dict[str, Any]) -> Dict[str, Any]: - url = f'https://{self.env.domain1}:{self.server_port}/reqs10.data' - fsize = 10*1024 + url = f'{self._scheme}://{self.env.domain1}:{self.server_port}/reqs10.data' + fsize = 10 * 1024 cols = ['size', 'total'] rows = [] mparallel = meta['request_parallels'] @@ -839,7 +842,7 @@ def run_score(args, protocol): server_port = env.h3_port else: server_descr = f'httpd/{env.httpd_version()}' - server_port = env.https_port + server_port = env.http_port if args.http_plain else env.https_port card = ScoreRunner(env=env, protocol=protocol, server_descr=server_descr, @@ -849,7 +852,8 @@ def run_score(args, protocol): upload_parallel=args.upload_parallel, with_flame=args.flame, socks_args=socks_args, - limit_rate=args.limit_rate) + limit_rate=args.limit_rate, + http_plain=args.http_plain) card.setup_resources(server_docs, downloads) cards.append(card) @@ -961,6 +965,8 @@ def main(): default = False, help="produce a flame graph on curl") parser.add_argument("--limit-rate", action='store', type=str, default=None, help="use curl's --limit-rate") + parser.add_argument("--http-plain", action='store_true', + default=False, help="run http: test instead of https:") parser.add_argument("-H", "--handshakes", action='store_true', default=False, help="evaluate handshakes only") diff --git a/tests/http/test_02_download.py b/tests/http/test_02_download.py index dc51c25f52dd..c75f932b7218 100644 --- a/tests/http/test_02_download.py +++ b/tests/http/test_02_download.py @@ -43,12 +43,12 @@ class TestDownload: def _class_scope(self, env, httpd): indir = httpd.docs_dir env.make_data_file(indir=indir, fname="data-0k", fsize=0) - env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) - env.make_data_file(indir=indir, fname="data-10m", fsize=10*1024*1024) - env.make_data_file(indir=indir, fname="data-50m", fsize=50*1024*1024) - env.make_data_gzipbomb(indir=indir, fname="bomb-100m.txt", fsize=100*1024*1024) + env.make_data_file(indir=indir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) + env.make_data_file(indir=indir, fname="data-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=indir, fname="data-50m", fsize=50 * 1024 * 1024) + env.make_data_gzipbomb(indir=indir, fname="bomb-100m.txt", fsize=100 * 1024 * 1024) # download 1 file @pytest.mark.parametrize("proto", Env.http_protos()) @@ -145,7 +145,7 @@ def test_02_07_download_reuse(self, env: Env, httpd, nghttpx, proto): ]) r.check_response(http_status=200, count=count) # should have used at most 2 connections only (test servers allow 100 req/conn) - # it may be just 1 on slow systems where request are answered faster than + # it may be 1 on slow systems where request are answered faster than # curl can exhaust the capacity or if curl runs with address-sanitizer speed assert r.total_connects <= 2, "h2 should use fewer connections here" @@ -275,7 +275,7 @@ def test_02_20_h2_small_frames(self, env: Env, httpd, configures_httpd): self.check_downloads(curl, srcfile, count) # download serial via lib client, pause/resume at different offsets - @pytest.mark.parametrize("pause_offset", [0, 10*1024, 100*1023, 640000]) + @pytest.mark.parametrize("pause_offset", [0, 10 * 1024, 100 * 1023, 640000]) @pytest.mark.parametrize("proto", Env.http_protos()) def test_02_21_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset): count = 2 @@ -293,7 +293,7 @@ def test_02_21_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset): self.check_downloads(client, srcfile, count) # download via lib client, several at a time, pause/resume - @pytest.mark.parametrize("pause_offset", [100*1023]) + @pytest.mark.parametrize("pause_offset", [100 * 1023]) @pytest.mark.parametrize("proto", Env.http_protos()) def test_02_22_lib_parallel_resume(self, env: Env, httpd, nghttpx, proto, pause_offset): count = 2 @@ -417,7 +417,7 @@ def test_02_25_h2_upgrade_x(self, env: Env, httpd): assert r.exit_code == 0, f'{client.dump_logs()}' # Special client that tests TLS session reuse in parallel transfers - # TODO: just uses a single connection for h2/h3. Not sure how to prevent that + # TODO: uses a single connection for h2/h3. Not sure how to prevent that @pytest.mark.parametrize("proto", Env.http_protos()) def test_02_26_session_shared_reuse(self, env: Env, proto, httpd, nghttpx): url = f'https://{env.authority_for(env.domain1, proto)}/data-100k' @@ -473,15 +473,17 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) assert False, f'download {dfile} differs:\n{diff}' # download via lib client, 1 at a time, pause/resume at different offsets - @pytest.mark.parametrize("pause_offset", [0, 10*1024, 100*1023, 640000]) + @pytest.mark.parametrize("pause_offset", [0, 10 * 1024, 100 * 1023, 640000]) @pytest.mark.parametrize("proto", Env.http_protos()) def test_02_29_h2_lib_serial(self, env: Env, httpd, nghttpx, proto, pause_offset): count = 2 @@ -688,7 +690,7 @@ def test_02_35_pause_bomb(self, env: Env, httpd, nghttpx, proto): # download with looong urls @pytest.mark.parametrize("proto", Env.http_protos()) - @pytest.mark.parametrize("url_junk", [1024, 16*1024, 32*1024, 64*1024, 80*1024, 96*1024]) + @pytest.mark.parametrize("url_junk", [1024, 16 * 1024, 32 * 1024, 64 * 1024, 80 * 1024, 96 * 1024]) def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): if proto == 'h3' and env.curl_uses_lib('quiche'): pytest.skip("quiche fails from 16k onwards") @@ -699,11 +701,11 @@ def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): if url_junk <= 1024: r.check_exit_code(0) r.check_response(http_status=200) - elif url_junk <= 16*1024: + elif url_junk <= 16 * 1024: r.check_exit_code(0) # server replies with 414, Request URL too long r.check_response(http_status=414) - elif url_junk <= 32*1024: + elif url_junk <= 32 * 1024: r.check_exit_code(0) # server replies with 414, Request URL too long r.check_response(http_status=414) @@ -716,7 +718,7 @@ def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk): # h2 is unable to send such large headers (frame limits) r.check_exit_code(55) elif proto == 'h3': - if url_junk <= 64*1024: + if url_junk <= 64 * 1024: r.check_exit_code(0) # nghttpx reports 431 Request Header Field too Large r.check_response(http_status=431) diff --git a/tests/http/test_05_errors.py b/tests/http/test_05_errors.py index a78b9c37463b..76c12bc8e551 100644 --- a/tests/http/test_05_errors.py +++ b/tests/http/test_05_errors.py @@ -43,7 +43,7 @@ class TestErrors: @pytest.mark.parametrize("proto", Env.http_protos()) def test_05_01_partial_1(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and env.curl_uses_lib('quiche') and \ - not env.curl_lib_version_at_least('quiche', '0.24.8'): + not env.curl_lib_version_at_least('quiche', '0.29.0'): pytest.skip("quiche issue #2277 not fixed") count = 1 curl = CurlClient(env=env) @@ -64,7 +64,7 @@ def test_05_01_partial_1(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_mplx_protos()) def test_05_02_partial_20(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and env.curl_uses_lib('quiche') and \ - not env.curl_lib_version_at_least('quiche', '0.24.8'): + not env.curl_lib_version_at_least('quiche', '0.29.0'): pytest.skip("quiche issue #2277 not fixed") count = 20 curl = CurlClient(env=env) @@ -100,7 +100,7 @@ def test_05_03_required(self, env: Env, httpd, nghttpx): assert r.stats[0]['http_version'] == '1.1', r.dump_logs() # On the URL used here, Apache is doing an "unclean" TLS shutdown, - # meaning it sends no shutdown notice and just closes TCP. + # meaning it sends no shutdown notice and closes TCP. # The HTTP response delivers a body without Content-Length. We expect: # - http/1.0 to fail since it relies on a clean connection close to # detect the end of the body @@ -197,6 +197,7 @@ def accept_and_close(): conn.recv(1) # wait for ClientHello conn.close() except Exception: + # ignore expected socket error pass t = threading.Thread(target=accept_and_close) diff --git a/tests/http/test_06_eyeballs.py b/tests/http/test_06_eyeballs.py index fb9df11d2f76..a423524fa5ef 100644 --- a/tests/http/test_06_eyeballs.py +++ b/tests/http/test_06_eyeballs.py @@ -214,3 +214,16 @@ def test_06_24_h3_altsvc_h2_used(self, env: Env, httpd, nghttpx): r.check_exit_code(0) r.check_response(count=1, http_status=200) assert r.stats[0]['http_version'] == '2' + + # h3 download using --connect-to IPv6 address + @pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support") + @pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6") + def test_06_25_h3_connect_to(self, env: Env, httpd, nghttpx): + curl = CurlClient(env=env, force_resolv=False) + urln = f'https://{env.authority_for(env.domain1, "h3")}/data.json' + r = curl.http_download(urls=[urln], extra_args=[ + '--http3-only', '--connect-to', + f'{env.authority_for(env.domain1, "h3")}:[::1]:{env.https_port}' + ]) + r.check_response(count=1, http_status=200) + assert r.stats[0]['http_version'] == '3' diff --git a/tests/http/test_07_upload.py b/tests/http/test_07_upload.py index 49f5213aa152..6cb4d943e155 100644 --- a/tests/http/test_07_upload.py +++ b/tests/http/test_07_upload.py @@ -42,12 +42,12 @@ class TestUpload: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd, nghttpx): - env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=env.gen_dir, fname="data-63k", fsize=63*1024) - env.make_data_file(indir=env.gen_dir, fname="data-64k", fsize=64*1024) - env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="data-1m+", fsize=(1024*1024)+1) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-63k", fsize=63 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-64k", fsize=64 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-1m+", fsize=(1024 * 1024) + 1) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) # upload small data, check that this is what was echoed @pytest.mark.parametrize("proto", Env.http_protos()) @@ -57,7 +57,8 @@ def test_07_01_upload_1_small(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' r = curl.http_upload(urls=[url], data=data, alpn_proto=proto) r.check_stats(count=1, http_status=200, exitcode=0) - respdata = open(curl.response_file(0)).readlines() + with open(curl.response_file(0)) as fr: + respdata = fr.readlines() assert respdata == [data] # upload large data, check that this is what was echoed @@ -68,8 +69,9 @@ def test_07_02_upload_1_large(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) r.check_stats(count=1, http_status=200, exitcode=0) - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata # upload data sequentially, check that they were echoed @@ -82,7 +84,8 @@ def test_07_10_upload_sequential(self, env: Env, httpd, nghttpx, proto): r = curl.http_upload(urls=[url], data=data, alpn_proto=proto) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [data] # upload data parallel, check that they were echoed @@ -97,7 +100,8 @@ def test_07_11_upload_parallel(self, env: Env, httpd, nghttpx, proto): extra_args=['--parallel']) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [data] # upload large data sequentially, check that this is what was echoed @@ -109,10 +113,12 @@ def test_07_12_upload_seq_large(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) r.check_response(count=count, http_status=200) - indata = open(fdata).readlines() + with open(fdata) as fi: + indata = fi.readlines() r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata # upload very large data sequentially, check that this is what was echoed @@ -124,9 +130,11 @@ def test_07_13_upload_seq_large(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) r.check_stats(count=count, http_status=200, exitcode=0) - indata = open(fdata).readlines() + with open(fdata) as fi: + indata = fi.readlines() for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata # upload from stdin, issue #14870 @@ -141,13 +149,14 @@ def test_07_14_upload_stdin(self, env: Env, httpd, nghttpx, proto, indata): r = curl.http_put(urls=[url], data=indata, alpn_proto=proto) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [f'{len(indata)}'] @pytest.mark.parametrize("proto", Env.http_protos()) def test_07_15_hx_put(self, env: Env, httpd, nghttpx, proto): count = 2 - upload_size = 128*1024 + upload_size = 128 * 1024 url = f'https://localhost:{env.https_port}/curltest/put' client = LocalClient(name='cli_hx_upload', env=env) if not client.exists(): @@ -161,7 +170,7 @@ def test_07_15_hx_put(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_protos()) def test_07_16_hx_put_reuse(self, env: Env, httpd, nghttpx, proto): count = 2 - upload_size = 128*1024 + upload_size = 128 * 1024 url = f'https://localhost:{env.https_port}/curltest/put' client = LocalClient(name='cli_hx_upload', env=env) if not client.exists(): @@ -175,7 +184,7 @@ def test_07_16_hx_put_reuse(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("proto", Env.http_protos()) def test_07_17_hx_post_reuse(self, env: Env, httpd, nghttpx, proto): count = 2 - upload_size = 128*1024 + upload_size = 128 * 1024 url = f'https://localhost:{env.https_port}/curltest/echo' client = LocalClient(name='cli_hx_upload', env=env) if not client.exists(): @@ -198,7 +207,8 @@ def test_07_20_upload_parallel(self, env: Env, httpd, nghttpx, proto): extra_args=['--parallel']) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [data] # upload large data parallel, check that this is what was echoed @@ -243,7 +253,8 @@ def test_07_30_put_100k(self, env: Env, httpd, nghttpx, proto): exp_data = [f'{os.path.getsize(fdata)}'] r.check_response(count=count, http_status=200) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == exp_data # PUT 10m @@ -259,7 +270,8 @@ def test_07_31_put_10m(self, env: Env, httpd, nghttpx, proto): exp_data = [f'{os.path.getsize(fdata)}'] r.check_response(count=count, http_status=200) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == exp_data # issue #10591 @@ -351,8 +363,9 @@ def test_07_35_h1_h2_upgrade_upload(self, env: Env, httpd, nghttpx): r.check_response(count=1, http_status=200) # apache does not Upgrade on request with a body assert r.stats[0]['http_version'] == '1.1', f'{r}' - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata # upload to a 301,302,303 response @@ -368,7 +381,8 @@ def test_07_36_upload_30x(self, env: Env, httpd, nghttpx, redir, proto): '-L', '--trace-config', 'http/2,http/3' ]) r.check_response(count=1, http_status=200) - respdata = open(curl.response_file(0)).readlines() + with open(curl.response_file(0)) as fr: + respdata = fr.readlines() assert respdata == [] # was transformed to a GET # upload to a 307 response @@ -383,7 +397,8 @@ def test_07_37_upload_307(self, env: Env, httpd, nghttpx, proto): '-L', '--trace-config', 'http/2,http/3' ]) r.check_response(count=1, http_status=200) - respdata = open(curl.response_file(0)).readlines() + with open(curl.response_file(0)) as fr: + respdata = fr.readlines() assert respdata == [data] # was POST again # POST form data, yet another code path in transfer @@ -406,8 +421,9 @@ def test_07_39_post_urlenc_small(self, env: Env, httpd, nghttpx, proto): '--trace-config', 'http/2,http/3' ]) r.check_stats(count=1, http_status=200, exitcode=0) - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata # POST data urlencoded, large enough to be sent separate from request headers @@ -420,8 +436,9 @@ def test_07_40_post_urlenc_large(self, env: Env, httpd, nghttpx, proto): '--trace-config', 'http/2,http/3' ]) r.check_stats(count=1, http_status=200, exitcode=0) - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata # POST data urlencoded, small enough to be sent with request headers @@ -442,8 +459,9 @@ def test_07_41_post_urlenc_small(self, env: Env, httpd, nghttpx, proto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, extra_args=extra_args) r.check_stats(count=1, http_status=200, exitcode=0) - indata = open(fdata).readlines() - respdata = open(curl.response_file(0)).readlines() + with open(fdata) as fi, open(curl.response_file(0)) as fr: + indata = fi.readlines() + respdata = fr.readlines() assert respdata == indata def check_download(self, r: ExecResult, count: int, srcfile: Union[str, os.PathLike], curl: CurlClient): @@ -451,8 +469,10 @@ def check_download(self, r: ExecResult, count: int, srcfile: Union[str, os.PathL dfile = curl.download_file(i) assert os.path.exists(dfile), f'download {dfile} missing\n{r.dump_logs()}' if not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -519,7 +539,7 @@ def test_07_43_upload_denied(self, env: Env, httpd, nghttpx, proto): @pytest.mark.parametrize("httpcode", [301, 302, 307, 308]) def test_07_44_put_redir(self, env: Env, httpd, nghttpx, proto, httpcode): count = 1 - upload_size = 128*1024 + upload_size = 128 * 1024 url = f'https://localhost:{env.https_port}/curltest/put-redir-{httpcode}' client = LocalClient(name='cli_hx_upload', env=env) if not client.exists(): @@ -639,10 +659,10 @@ def test_07_63_upload_exp100_paused(self, env: Env, httpd, nghttpx, proto): @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx") @pytest.mark.parametrize("proto,upload_size", [ pytest.param('http/1.1', 100, id='h1-small-body'), - pytest.param('http/1.1', 10*1024, id='h1-medium-body'), - pytest.param('http/1.1', 32*1024, id='h1-limited-body'), - pytest.param('h2', 10*1024, id='h2-medium-body'), - pytest.param('h2', 32*1024, id='h2-limited-body'), + pytest.param('http/1.1', 10 * 1024, id='h1-medium-body'), + pytest.param('http/1.1', 32 * 1024, id='h1-limited-body'), + pytest.param('h2', 10 * 1024, id='h2-medium-body'), + pytest.param('h2', 32 * 1024, id='h2-limited-body'), pytest.param('h3', 1024, id='h3-small-body'), pytest.param('h3', 1024 * 1024, id='h3-limited-body'), ]) @@ -699,8 +719,9 @@ def check_downloads(self, client, r, source: List[str], count: int, dfile = client.download_file(i) assert os.path.exists(dfile), f'download {dfile} missing\n{r.dump_logs()}' if complete: - diff = "".join(difflib.unified_diff(a=source, - b=open(dfile).readlines(), + with open(dfile) as fb: + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=source, b=b, fromfile='-', tofile=dfile, n=1)) diff --git a/tests/http/test_08_caddy.py b/tests/http/test_08_caddy.py index c7dd25a8eb5d..23cea4582ab8 100644 --- a/tests/http/test_08_caddy.py +++ b/tests/http/test_08_caddy.py @@ -59,12 +59,12 @@ def _make_docs_file(self, docs_dir: str, fname: str, fsize: int): @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, caddy): - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data10k.data', fsize=10*1024) - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data1.data', fsize=1024*1024) - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data5.data', fsize=5*1024*1024) - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data10.data', fsize=10*1024*1024) - self._make_docs_file(docs_dir=caddy.docs_dir, fname='data100.data', fsize=100*1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data10k.data', fsize=10 * 1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data1.data', fsize=1024 * 1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data5.data', fsize=5 * 1024 * 1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data10.data', fsize=10 * 1024 * 1024) + self._make_docs_file(docs_dir=caddy.docs_dir, fname='data100.data', fsize=100 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) # download 1 file @pytest.mark.parametrize("proto", Env.http_protos()) @@ -151,7 +151,8 @@ def test_08_06_post_parallel(self, env: Env, httpd, caddy, proto): extra_args=['--parallel']) r.check_stats(count=count, http_status=200, exitcode=0) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == [data] # put large file, check that they length were echoed @@ -166,7 +167,8 @@ def test_08_07_put_large(self, env: Env, httpd, caddy, proto): exp_data = [f'{os.path.getsize(fdata)}'] r.check_response(count=count, http_status=200) for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == exp_data @pytest.mark.parametrize("proto", Env.http_protos()) @@ -210,8 +212,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) diff --git a/tests/http/test_09_push.py b/tests/http/test_09_push.py index 8f4714be0708..386289b39e97 100644 --- a/tests/http/test_09_push.py +++ b/tests/http/test_09_push.py @@ -40,9 +40,9 @@ def _class_scope(self, env, httpd): push_dir = os.path.join(httpd.docs_dir, 'push') if not os.path.exists(push_dir): os.makedirs(push_dir) - env.make_data_file(indir=push_dir, fname="data1", fsize=1*1024) - env.make_data_file(indir=push_dir, fname="data2", fsize=1*1024) - env.make_data_file(indir=push_dir, fname="data3", fsize=1*1024) + env.make_data_file(indir=push_dir, fname="data1", fsize=1 * 1024) + env.make_data_file(indir=push_dir, fname="data2", fsize=1 * 1024) + env.make_data_file(indir=push_dir, fname="data3", fsize=1 * 1024) def httpd_configure(self, env, httpd): httpd.set_extra_config(env.domain1, [ diff --git a/tests/http/test_10_proxy.py b/tests/http/test_10_proxy.py index b1840b484d7a..5843c30b08f1 100644 --- a/tests/http/test_10_proxy.py +++ b/tests/http/test_10_proxy.py @@ -45,11 +45,11 @@ def _class_scope(self, env, httpd, nghttpx_fwd): os.makedirs(push_dir) if env.have_nghttpx(): nghttpx_fwd.start_if_needed() - env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) def get_tunnel_proto_used(self, r: ExecResult): for line in r.trace_lines: @@ -57,7 +57,6 @@ def get_tunnel_proto_used(self, r: ExecResult): if m: return m.group(1) assert False, f'tunnel protocol not found in:\n{"".join(r.trace_lines)}' - return None # download via http: proxy (no tunnel) def test_10_01_proxy_http(self, env: Env, httpd): @@ -85,15 +84,16 @@ def test_10_02_proxys_down(self, env: Env, httpd, proto): # upload via https: with proto (no tunnel) @pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL") @pytest.mark.parametrize("proto", Env.http_h1_h2_protos()) - @pytest.mark.parametrize("fname, fcount", [ - ['data.json', 5], - ['data-100k', 5], - ['data-1m', 2] + @pytest.mark.parametrize("fname, fcount, with_alpn", [ + ['data.json', 5, False], + ['data.json', 5, True], + ['data-100k', 5, True], + ['data-1m', 2, True] ]) @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available") def test_10_02_proxys_up(self, env: Env, httpd, nghttpx, proto, - fname, fcount): + fname, fcount, with_alpn): if proto == 'h2' and not env.curl_uses_lib('nghttp2'): pytest.skip('only supported with nghttp2') count = fcount @@ -101,13 +101,17 @@ def test_10_02_proxys_up(self, env: Env, httpd, nghttpx, proto, curl = CurlClient(env=env) url = f'http://localhost:{env.http_port}/curltest/echo?id=[0-{count-1}]' xargs = curl.get_proxy_args(proto=proto) + if not with_alpn: + xargs.append('--no-alpn') r = curl.http_upload(urls=[url], data=f'@{srcfile}', alpn_proto=proto, extra_args=xargs) r.check_response(count=count, http_status=200, protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1') - indata = open(srcfile).readlines() + with open(srcfile) as fi: + indata = fi.readlines() for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata # download http: via http: proxytunnel @@ -229,9 +233,11 @@ def test_10_08_upload_seq_large(self, env: Env, httpd, nghttpx, proto, assert self.get_tunnel_proto_used(r) == tunnel r.check_response(count=count, http_status=200) assert r.total_connects == 1, r.dump_logs() - indata = open(srcfile).readlines() + with open(srcfile) as fi: + indata = fi.readlines() for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata, f'response {i} differs' @pytest.mark.skipif(condition=not Env.have_ssl_curl(), reason="curl without SSL") @@ -397,7 +403,7 @@ def test_10_15_proxy_ip_addr(self, env: Env, httpd): xargs.append('-6') r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, extra_args=xargs) - r.check_exit_code(0), f'{r}' + r.check_exit_code(0) r.check_response(count=1, http_status=200, protocol='HTTP/1.1') # download via http: ipv6 proxy (no tunnel) using IP address, IPv4 only @@ -411,5 +417,15 @@ def test_10_16_proxy_ip_addr(self, env: Env, httpd): xargs.append('-4') r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, extra_args=xargs) - r.check_exit_code(0), f'{r}' + r.check_exit_code(0) r.check_response(count=1, http_status=200, protocol='HTTP/1.1') + + # download via http: proxy (no tunnel), check connection reuse + def test_10_17_proxy_http(self, env: Env, httpd): + curl = CurlClient(env=env) + url1 = f'http://localhost:{env.http_port}/data.json' + url2 = f'http://127.0.0.1:{env.http_port}/data.json' + r = curl.http_download(urls=[url1, url2], alpn_proto='http/1.1', with_stats=True, + extra_args=curl.get_proxy_args(proxys=False)) + r.check_response(count=2, http_status=200) + assert r.total_connects == 1, r.dump_logs() diff --git a/tests/http/test_11_unix.py b/tests/http/test_11_unix.py index fe99512a0473..774a7737c91b 100644 --- a/tests/http/test_11_unix.py +++ b/tests/http/test_11_unix.py @@ -136,3 +136,16 @@ def test_11_03_unix_connect_quic(self, env: Env, httpd, uds_faker): r.check_response(exitcode=96, http_status=None) assert r.stats[0]['remote_port'] == -1, f'{r.dump_logs()}' assert r.stats[0]['local_port'] == -1, f'{r.dump_logs()}' + + # download http: via Unix socket, ignore proxy args + def test_11_04_unix_connect_http(self, env: Env, httpd, uds_faker): + curl = CurlClient(env=env) + url = f'http://{env.domain1}:{env.http_port}/data.json' + xargs = curl.get_proxy_args(proto='http/1.1', use_ip=True, proxys=False) + xargs.extend([ + '--unix-socket', uds_faker.path, + ]) + r = curl.http_download(urls=[url], with_stats=True, extra_args=xargs) + r.check_response(count=1, http_status=200) + assert r.stats[0]['remote_port'] == -1, f'{r.dump_logs()}' + assert r.stats[0]['local_port'] == -1, f'{r.dump_logs()}' diff --git a/tests/http/test_13_proxy_auth.py b/tests/http/test_13_proxy_auth.py index d74a9f280b76..50e45960575d 100644 --- a/tests/http/test_13_proxy_auth.py +++ b/tests/http/test_13_proxy_auth.py @@ -48,7 +48,6 @@ def get_tunnel_proto_used(self, r: ExecResult): if m: return m.group(1) assert False, f'tunnel protocol not found in:\n{"".join(r.trace_lines)}' - return None # download via http: proxy (no tunnel), no auth def test_13_01_proxy_no_auth(self, env: Env, httpd, configures_httpd): diff --git a/tests/http/test_14_auth.py b/tests/http/test_14_auth.py index 288e7b5d28de..69866baedf98 100644 --- a/tests/http/test_14_auth.py +++ b/tests/http/test_14_auth.py @@ -37,7 +37,7 @@ class TestAuth: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd, nghttpx): - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) # download 1 file, not authenticated @pytest.mark.parametrize("proto", Env.http_protos()) @@ -95,7 +95,7 @@ def test_14_05_basic_large_pw(self, env: Env, httpd, nghttpx, proto): if proto == 'h3' and not env.curl_uses_lib('ngtcp2'): # See pytest.skip("quiche has problems with large requests") - # just large enough that nghttp2 will submit + # large enough that nghttp2 will submit password = 'x' * (47 * 1024) fdata = os.path.join(env.gen_dir, 'data-10m') curl = CurlClient(env=env) diff --git a/tests/http/test_16_info.py b/tests/http/test_16_info.py index 5be5e31b92ca..977342eb55f8 100644 --- a/tests/http/test_16_info.py +++ b/tests/http/test_16_info.py @@ -38,10 +38,10 @@ class TestInfo: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd): indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100*1024) + env.make_data_file(indir=indir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-100k", fsize=100 * 1024) # download plain file @pytest.mark.parametrize("proto", Env.http_protos()) diff --git a/tests/http/test_17_ssl_use.py b/tests/http/test_17_ssl_use.py index b2339dab5165..4179925f42eb 100644 --- a/tests/http/test_17_ssl_use.py +++ b/tests/http/test_17_ssl_use.py @@ -64,7 +64,7 @@ class TestSSLUse: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd, nghttpx): - env.make_data_file(indir=httpd.docs_dir, fname="data-10k", fsize=10*1024) + env.make_data_file(indir=httpd.docs_dir, fname="data-10k", fsize=10 * 1024) def test_17_01_sslinfo_plain(self, env: Env, httpd): proto = 'http/1.1' @@ -127,7 +127,7 @@ def test_17_03_trailing_dot(self, env: Env, proto, httpd, nghttpx): # the SNI the server received is without trailing dot assert r.json['SSL_TLS_SNI'] == env.domain1, f'{r.json}' - # use hostname with double trailing dot, verify handshake + # use hostname with double trailing dot @pytest.mark.parametrize("proto", Env.http_protos()) def test_17_04_double_dot(self, env: Env, proto, httpd, nghttpx): curl = CurlClient(env=env) @@ -142,10 +142,8 @@ def test_17_04_double_dot(self, env: Env, proto, httpd, nghttpx): if proto != 'h3': # we proxy h3 assert r.json['SSL_TLS_SNI'] == env.domain1, f'{r.json}' assert False, f'should not have succeeded: {r.json}' - # 7 - Rustls rejects a servername with .. during setup - # 35 - LibreSSL rejects setting an SNI name with trailing dot - # 60 - peer name matching failed against certificate - assert r.exit_code in [7, 35, 60], f'{r}' + # 3 - not allowed in the URL + assert r.exit_code in [3], f'{r}' # use ip address for connect @pytest.mark.parametrize("proto", Env.http_protos()) @@ -245,10 +243,10 @@ def test_17_07_ssl_ciphers(self, env: Env, httpd, configures_httpd, succeed13, succeed12): # to test setting cipher suites, the AES 256 ciphers are disabled in the test server httpd.set_extra_config('base', [ - 'SSLCipherSuite SSL' - ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' + 'SSLCipherSuite SSL' + + ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' + ':ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305', - 'SSLCipherSuite TLSv1.3' + 'SSLCipherSuite TLSv1.3' + ' TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256', f'SSLProtocol {tls_proto}' ]) @@ -527,10 +525,10 @@ def test_17_17_h1_ignore_ciphers13(self, env: Env, httpd): def test_17_18_gnutls_priority(self, env: Env, httpd, configures_httpd, priority, tls_proto, ciphers, success): # to test setting cipher suites, the AES 256 ciphers are disabled in the test server httpd.set_extra_config('base', [ - 'SSLCipherSuite SSL' - ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' + 'SSLCipherSuite SSL' + + ' ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256' + ':ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305', - 'SSLCipherSuite TLSv1.3' + 'SSLCipherSuite TLSv1.3' + ' TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256', ]) httpd.reload_if_config_changed() diff --git a/tests/http/test_18_methods.py b/tests/http/test_18_methods.py index 006347092833..faa31a638e7f 100644 --- a/tests/http/test_18_methods.py +++ b/tests/http/test_18_methods.py @@ -37,9 +37,9 @@ class TestMethods: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd, nghttpx): indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) + env.make_data_file(indir=indir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) # download 1 file @pytest.mark.parametrize("proto", Env.http_protos()) diff --git a/tests/http/test_19_shutdown.py b/tests/http/test_19_shutdown.py index d72ed5a2ece2..4ed9ad27fb01 100644 --- a/tests/http/test_19_shutdown.py +++ b/tests/http/test_19_shutdown.py @@ -39,9 +39,9 @@ class TestShutdown: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd): indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=indir, fname="data-100k", fsize=100*1024) - env.make_data_file(indir=indir, fname="data-1m", fsize=1024*1024) + env.make_data_file(indir=indir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=indir, fname="data-100k", fsize=100 * 1024) + env.make_data_file(indir=indir, fname="data-1m", fsize=1024 * 1024) # check with `tcpdump` that we see curl TCP RST packets @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") diff --git a/tests/http/test_20_websockets.py b/tests/http/test_20_websockets.py index fdc9df6eb7d2..0a86e6439b1e 100644 --- a/tests/http/test_20_websockets.py +++ b/tests/http/test_20_websockets.py @@ -24,11 +24,15 @@ # ########################################################################### # +import base64 +import hashlib import logging import os +import re import shutil import socket import subprocess +import threading import time from datetime import datetime, timedelta from typing import Dict @@ -176,7 +180,7 @@ def test_20_07_data_large_small_recv(self, env: Env, ws_echo, model): r.check_exit_code(0) # Send large frames and simulate send blocking on 8192 bytes chunks - # Simlates error reported in #15865 + # Simulates error reported in #15865 @pytest.mark.parametrize("model", [ pytest.param(1, id='multi_perform'), pytest.param(2, id='curl_ws_send+recv'), @@ -206,3 +210,93 @@ def test_20_09_data_empty(self, env: Env, ws_echo, model): large = 0 r = client.run(args=[f'-{model}', '-c', str(count), '-m', str(large), url]) r.check_exit_code(0) + + # use ws:// url with HTTP proxy, check that it tunnels automatically + def test_20_10_proxy_http(self, env: Env, httpd, ws_echo): + curl = CurlClient(env=env) + url = f'ws://127.0.0.1:{env.ws_port}/' + xargs = curl.get_proxy_args(proxys=False) + xargs.extend([ + '--max-time', '2' + ]) + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + extra_args=xargs) + # The CONNECT through the proxy fails as it does not allow it + r.check_exit_code(7) # CURLE_COULDNT_CONNECT + assert r.stats[0]['http_connect'] == 403, f'{r}' + + def test_20_11_crazy_pings(self, env: Env): + st = {} + send_rounds = 1 + + def srv(): + try: + with socket.socket() as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(("127.0.0.1", 0)) + s.listen(1) + st["p"] = s.getsockname()[1] + + c, _ = s.accept() + c.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4096) + c.settimeout(Env.SERVER_TIMEOUT) + req = b"" + while b"\r\n\r\n" not in req: + req += c.recv(4096) + + k = re.search(rb"(?im)^Sec-WebSocket-Key:\s*(\S+)", req).group(1) + a = base64.b64encode( + hashlib.sha1(k + b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest() + ).decode() + c.sendall( + ( + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + f"Sec-WebSocket-Accept: {a}\r\n\r\n" + ).encode() + ) + + f = b"\x89\x00" * 65536 # PING frames, many + try: + for _ in range(send_rounds): + c.sendall(f) + f = b"\x88\x00" # CLOSE frame + c.sendall(f) + except OSError: + # Client may close/reset while we intentionally flood frames. + # Send errors are expected here, ignore them. + pass + time.sleep(1) + c.close() + except OSError as e: + st["err"] = e + + curl = CurlClient(env=env) + send_rounds = 2 + threading.Thread(target=srv, daemon=True).start() + while "p" not in st and "err" not in st: + time.sleep(0.01) + assert "err" not in st, f'ws-ping server failed to start: {st["err"]}' + + url = f'ws://127.0.0.1:{st["p"]}/' + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + with_profile=True) + assert r.exit_code in [55, 56], f'{r.dump_logs()}' # SEND/RECV_ERROR + assert r.profile, f'{r}' + rss1 = r.profile.stats['rss'] / (1024 * 1024) + + st.clear() + send_rounds = 10 + threading.Thread(target=srv, daemon=True).start() + while "p" not in st and "err" not in st: + time.sleep(0.01) + assert "err" not in st, f'ws-ping server failed to start: {st["err"]}' + + url = f'ws://127.0.0.1:{st["p"]}/' + r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True, + with_profile=True) + assert r.exit_code in [55, 56], f'{r.dump_logs()}' # SEND/RECV_ERROR + assert r.profile, f'{r}' + rss2 = r.profile.stats['rss'] / (1024 * 1024) + assert (rss1 * 1.1) >= rss2, 'bad memory increase' diff --git a/tests/http/test_21_resolve.py b/tests/http/test_21_resolve.py index b55f066f2d25..8f2937f42f1e 100644 --- a/tests/http/test_21_resolve.py +++ b/tests/http/test_21_resolve.py @@ -30,7 +30,7 @@ from typing import Generator import pytest -from testenv import CurlClient, Env, LocalClient, Dnsd +from testenv import CurlClient, Dnsd, Env, LocalClient log = logging.getLogger(__name__) @@ -132,7 +132,7 @@ def test_21_06_dnsd_empty(self, env: Env, httpd, dnsd): run_env = os.environ.copy() run_env['CURL_DNS_SERVER'] = f'127.0.0.1:{dnsd.port}' curl = CurlClient(env=env, run_env=run_env, force_resolv=False) - url = f'https://test-dnsd.http.curl.invalid/' + url = 'https://test-dnsd.http.curl.invalid/' r = curl.http_download(urls=[url], with_stats=True) r.check_exit_code(6) # could not resolve host r.check_stats(count=1, http_status=0, exitcode=6) diff --git a/tests/http/test_22_httpsrr.py b/tests/http/test_22_httpsrr.py index 61f5e2e2f73f..51ed3bbd9dec 100644 --- a/tests/http/test_22_httpsrr.py +++ b/tests/http/test_22_httpsrr.py @@ -29,7 +29,7 @@ from typing import Generator import pytest -from testenv import CurlClient, Env, Dnsd +from testenv import CurlClient, Dnsd, Env log = logging.getLogger(__name__) diff --git a/tests/http/test_30_vsftpd.py b/tests/http/test_30_vsftpd.py index 5991c4555343..d625852249a8 100644 --- a/tests/http/test_30_vsftpd.py +++ b/tests/http/test_30_vsftpd.py @@ -64,20 +64,21 @@ def _class_scope(self, env, vsftpd): os.makedirs(vsftpd.docs_dir) self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-0k', fsize=0) self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-1k', fsize=1024) - self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10k', fsize=10*1024) - self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-1m', fsize=1024*1024) - self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10m', fsize=10*1024*1024) + self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10k', fsize=10 * 1024) + self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-1m', fsize=1024 * 1024) + self._make_docs_file(docs_dir=vsftpd.docs_dir, fname='data-10m', fsize=10 * 1024 * 1024) env.make_data_file(indir=env.gen_dir, fname="upload-0k", fsize=0) env.make_data_file(indir=env.gen_dir, fname="upload-1k", fsize=1024) - env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024*1024) + env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024 * 1024) def test_30_01_list_dir(self, env: Env, vsftpd: VsFTPD): curl = CurlClient(env=env) url = f'ftp://{env.ftp_domain}:{vsftpd.port}/' r = curl.ftp_get(urls=[url], with_stats=True) r.check_stats(count=1, http_status=226) - lines = open(os.path.join(curl.run_dir, 'download_#1.data')).readlines() + with open(os.path.join(curl.run_dir, 'download_#1.data')) as fd: + lines = fd.readlines() assert len(lines) == 5, f'list: {lines}' r.check_stats_timelines() @@ -148,7 +149,7 @@ def _rmf(self, path): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_30_06_shutdownh_download(self, env: Env, vsftpd: VsFTPD): + def test_30_06_shutdown_download(self, env: Env, vsftpd: VsFTPD): docname = 'data-1k' curl = CurlClient(env=env) count = 1 @@ -166,7 +167,7 @@ def test_30_06_shutdownh_download(self, env: Env, vsftpd: VsFTPD): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_30_07_shutdownh_upload(self, env: Env, vsftpd: VsFTPD): + def test_30_07_shutdown_upload(self, env: Env, vsftpd: VsFTPD): docname = 'upload-1k' curl = CurlClient(env=env) srcfile = os.path.join(env.gen_dir, docname) @@ -251,8 +252,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -264,8 +267,10 @@ def check_upload(self, env, vsftpd: VsFTPD, docname, binary=True): assert os.path.exists(srcfile) assert os.path.exists(dstfile) if not filecmp.cmp(srcfile, dstfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dstfile).readlines(), + with open(srcfile) as fa, open(dstfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dstfile, n=1)) diff --git a/tests/http/test_31_vsftpds.py b/tests/http/test_31_vsftpds.py index 0f8f23e9b6fa..ac4c154c4722 100644 --- a/tests/http/test_31_vsftpds.py +++ b/tests/http/test_31_vsftpds.py @@ -70,19 +70,20 @@ def _class_scope(self, env, vsftpds): if not os.path.exists(vsftpds.docs_dir): os.makedirs(vsftpds.docs_dir) self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1k', fsize=1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10*1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024*1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10*1024*1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10 * 1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024 * 1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10 * 1024 * 1024) env.make_data_file(indir=env.gen_dir, fname="upload-1k", fsize=1024) - env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024*1024) + env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024 * 1024) def test_31_01_list_dir(self, env: Env, vsftpds: VsFTPD): curl = CurlClient(env=env) url = f'ftp://{env.ftp_domain}:{vsftpds.port}/' r = curl.ftp_ssl_get(urls=[url], with_stats=True) r.check_stats(count=1, http_status=226) - lines = open(os.path.join(curl.run_dir, 'download_#1.data')).readlines() + with open(os.path.join(curl.run_dir, 'download_#1.data')) as fd: + lines = fd.readlines() assert len(lines) == 4, f'list: {lines}' r.check_stats_timelines() @@ -153,7 +154,7 @@ def _rmf(self, path): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_31_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD): + def test_31_06_shutdown_download(self, env: Env, vsftpds: VsFTPD): docname = 'data-1k' curl = CurlClient(env=env) count = 1 @@ -170,7 +171,7 @@ def test_31_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_31_07_shutdownh_upload(self, env: Env, vsftpds: VsFTPD): + def test_31_07_shutdown_upload(self, env: Env, vsftpds: VsFTPD): docname = 'upload-1k' curl = CurlClient(env=env) srcfile = os.path.join(env.gen_dir, docname) @@ -191,7 +192,7 @@ def test_31_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): line_length = 21 srcfile = os.path.join(env.gen_dir, docname) dstfile = os.path.join(vsftpds.docs_dir, docname) - env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100*1024, + env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100 * 1024, line_length=line_length) srcsize = os.path.getsize(srcfile) self._rmf(dstfile) @@ -203,7 +204,8 @@ def test_31_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): r.check_stats(count=count, http_status=226) # expect the uploaded file to be number of converted newlines larger dstsize = os.path.getsize(dstfile) - newlines = len(open(srcfile).readlines()) + with open(srcfile) as fd: + newlines = len(fd.readlines()) assert (srcsize + newlines) == dstsize, \ f'expected source with {newlines} lines to be that much larger,'\ f'instead srcsize={srcsize}, upload size={dstsize}, diff={dstsize-srcsize}' @@ -248,7 +250,8 @@ def test_31_10_upload_stdin(self, env: Env, vsftpds: VsFTPD, indata): r = curl.ftp_ssl_upload(urls=[url], updata=indata, with_stats=True) r.check_stats(count=count, http_status=226) assert os.path.exists(dstfile) - destdata = open(dstfile).readlines() + with open(dstfile) as fd: + destdata = fd.readlines() expdata = [indata] if len(indata) else [] assert expdata == destdata, f'expected: {expdata}, got: {destdata}' @@ -270,14 +273,40 @@ def test_31_12_upload_eprt(self, env: Env, vsftpds: VsFTPD): dstfile = os.path.join(vsftpds.docs_dir, docname) assert os.path.exists(dstfile), f'{r.dump_logs()}' + # connection reuse with STARTTLS required + # 1st download without STARTTLS, 2nd with --ssl-reqd + @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") + def test_31_13_starttls_reuse(self, env: Env, vsftpds: VsFTPD): + run_env = os.environ.copy() + run_env['CURL_DBG_NO_USE_SSL_ON_FIRST'] = '1' + curl = CurlClient(env=env, run_env=run_env) + url1 = f'ftp://{env.ftp_domain}:{vsftpds.port}/data-1k' + url2 = f'ftp://{env.ftp_domain}:{vsftpds.port}/data-10k' + r = curl.run_direct(with_stats=True, args=[ + '-svv', '--resolve', f'{env.ftp_domain}:{vsftpds.port}:127.0.0.1', + '--cacert', env.ca.cert_file, + url1, '--out-null', + url2, '--out-null', '--ssl-reqd' + ]) + r.check_exit_code(0) + r.check_stats(count=2, http_status=226) + # expect 4 connections to have been made: + # 1. 1st CONTROL without STARTTLS + # 2. 1st DATA for download + # 3. 2nd CONTROL with STARTTLS (not reuse of 1) + # 4. 2nd DATA for download + assert r.total_connects == 4, f'{r.dump_logs()}' + def check_downloads(self, client, srcfile: str, count: int, complete: bool = True): for i in range(count): dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -289,8 +318,10 @@ def check_upload(self, env, vsftpd: VsFTPD, docname): assert os.path.exists(srcfile) assert os.path.exists(dstfile) if not filecmp.cmp(srcfile, dstfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dstfile).readlines(), + with open(srcfile) as fa, open(dstfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dstfile, n=1)) diff --git a/tests/http/test_32_ftps_vsftpd.py b/tests/http/test_32_ftps_vsftpd.py index 5433081d9822..1181bb3211ce 100644 --- a/tests/http/test_32_ftps_vsftpd.py +++ b/tests/http/test_32_ftps_vsftpd.py @@ -70,19 +70,20 @@ def _class_scope(self, env, vsftpds): if not os.path.exists(vsftpds.docs_dir): os.makedirs(vsftpds.docs_dir) self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1k', fsize=1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10*1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024*1024) - self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10*1024*1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10k', fsize=10 * 1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-1m', fsize=1024 * 1024) + self._make_docs_file(docs_dir=vsftpds.docs_dir, fname='data-10m', fsize=10 * 1024 * 1024) env.make_data_file(indir=env.gen_dir, fname="upload-1k", fsize=1024) - env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100*1024) - env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024*1024) + env.make_data_file(indir=env.gen_dir, fname="upload-100k", fsize=100 * 1024) + env.make_data_file(indir=env.gen_dir, fname="upload-1m", fsize=1024 * 1024) def test_32_01_list_dir(self, env: Env, vsftpds: VsFTPD): curl = CurlClient(env=env) url = f'ftps://{env.ftp_domain}:{vsftpds.port}/' r = curl.ftp_get(urls=[url], with_stats=True) r.check_stats(count=1, http_status=226) - lines = open(os.path.join(curl.run_dir, 'download_#1.data')).readlines() + with open(os.path.join(curl.run_dir, 'download_#1.data')) as fd: + lines = fd.readlines() assert len(lines) == 4, f'list: {lines}' r.check_stats_timelines() @@ -166,7 +167,7 @@ def _rmf(self, path): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_32_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD): + def test_32_06_shutdown_download(self, env: Env, vsftpds: VsFTPD): docname = 'data-1k' curl = CurlClient(env=env) count = 1 @@ -183,7 +184,7 @@ def test_32_06_shutdownh_download(self, env: Env, vsftpds: VsFTPD): @pytest.mark.skipif(condition=not Env.tcpdump(), reason="tcpdump not available") @pytest.mark.skipif(condition=not Env.curl_is_debug(), reason="needs curl debug") @pytest.mark.skipif(condition=not Env.curl_is_verbose(), reason="needs curl verbose strings") - def test_32_07_shutdownh_upload(self, env: Env, vsftpds: VsFTPD): + def test_32_07_shutdown_upload(self, env: Env, vsftpds: VsFTPD): docname = 'upload-1k' curl = CurlClient(env=env) srcfile = os.path.join(env.gen_dir, docname) @@ -204,7 +205,7 @@ def test_32_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): line_length = 21 srcfile = os.path.join(env.gen_dir, docname) dstfile = os.path.join(vsftpds.docs_dir, docname) - env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100*1024, + env.make_data_file(indir=env.gen_dir, fname=docname, fsize=100 * 1024, line_length=line_length) srcsize = os.path.getsize(srcfile) self._rmf(dstfile) @@ -216,7 +217,8 @@ def test_32_08_upload_ascii(self, env: Env, vsftpds: VsFTPD): r.check_stats(count=count, http_status=226) # expect the uploaded file to be number of converted newlines larger dstsize = os.path.getsize(dstfile) - newlines = len(open(srcfile).readlines()) + with open(srcfile) as fd: + newlines = len(fd.readlines()) assert (srcsize + newlines) == dstsize, \ f'expected source with {newlines} lines to be that much larger,'\ f'instead srcsize={srcsize}, upload size={dstsize}, diff={dstsize-srcsize}' @@ -261,7 +263,8 @@ def test_32_10_upload_stdin(self, env: Env, vsftpds: VsFTPD, indata): r = curl.ftp_upload(urls=[url], updata=indata, with_stats=True) r.check_stats(count=count, http_status=226) assert os.path.exists(dstfile) - destdata = open(dstfile).readlines() + with open(dstfile) as fd: + destdata = fd.readlines() expdata = [indata] if len(indata) else [] assert expdata == destdata, f'expected: {expdata}, got: {destdata}' @@ -289,8 +292,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -302,8 +307,10 @@ def check_upload(self, env, vsftpd: VsFTPD, docname): assert os.path.exists(srcfile) assert os.path.exists(dstfile) if not filecmp.cmp(srcfile, dstfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dstfile).readlines(), + with open(srcfile) as fa, open(dstfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dstfile, n=1)) diff --git a/tests/http/test_40_socks.py b/tests/http/test_40_socks.py index 0e1d117399c4..f4ae322f7321 100644 --- a/tests/http/test_40_socks.py +++ b/tests/http/test_40_socks.py @@ -49,8 +49,8 @@ def danted(self, env: Env) -> Generator[Dante, None, None]: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, httpd): indir = httpd.docs_dir - env.make_data_file(indir=indir, fname="data-10m", fsize=10*1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=indir, fname="data-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) @pytest.mark.parametrize("sproto", ['socks4', 'socks5']) def test_40_01_socks_http(self, env: Env, sproto, danted: Dante, httpd): @@ -74,6 +74,24 @@ def test_40_02_socks_https(self, env: Env, sproto, proto, danted: Dante, httpd): else: r.check_response(http_status=200) + # download via socks to https: proxy (no tunnel) + @pytest.mark.parametrize("sproto", ['socks4', 'socks5']) + @pytest.mark.parametrize("proto", Env.http_h1_h2_protos()) + def test_40_02b_socks_https_proxy(self, env: Env, sproto, proto, danted: Dante, httpd): + if proto == 'h2' and not env.curl_uses_lib('nghttp2'): + pytest.skip('only supported with nghttp2') + curl = CurlClient(env=env, socks_args=[ + f'--{sproto}', f'127.0.0.1:{danted.port}' + ]) + url = f'http://localhost:{env.http_port}/data.json' + xargs = curl.get_proxy_args(proto=proto, tunnel=False) + r = curl.http_download(urls=[url], alpn_proto=proto, with_stats=True, + extra_args=xargs) + r.check_response(http_status=200) + exp_http_version = '2' if proto == 'h2' else '1.1' + assert r.stats[0]['proxy_used'] == 1, f'{r}' + assert r.stats[0]['http_version'] == exp_http_version, f'{r}' + @pytest.mark.parametrize("sproto", ['socks4', 'socks5']) @pytest.mark.parametrize("proto", Env.http_h1_h2_protos()) def test_40_03_dl_serial(self, env: Env, httpd, danted, proto, sproto): @@ -96,7 +114,9 @@ def test_40_04_ul_serial(self, env: Env, httpd, danted, proto, sproto): url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-{count-1}]' r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto) r.check_stats(count=count, http_status=200, exitcode=0) - indata = open(fdata).readlines() + with open(fdata) as fi: + indata = fi.readlines() for i in range(count): - respdata = open(curl.response_file(i)).readlines() + with open(curl.response_file(i)) as fr: + respdata = fr.readlines() assert respdata == indata diff --git a/tests/http/test_50_scp.py b/tests/http/test_50_scp.py index 409378a11ee7..831ee5b3f80e 100644 --- a/tests/http/test_50_scp.py +++ b/tests/http/test_50_scp.py @@ -41,10 +41,10 @@ class TestScp: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, sshd): - env.make_data_file(indir=sshd.home_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=sshd.home_dir, fname="data-10m", fsize=10*1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=sshd.home_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=sshd.home_dir, fname="data-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) def test_50_01_insecure(self, env: Env, sshd: Sshd): curl = CurlClient(env=env) @@ -191,8 +191,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -202,8 +204,10 @@ def check_upload(self, sshd: Sshd, srcfile, destfile, binary=True): assert os.path.exists(srcfile) assert os.path.exists(destfile) if not filecmp.cmp(srcfile, destfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(destfile).readlines(), + with open(srcfile) as fa, open(destfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=destfile, n=1)) diff --git a/tests/http/test_51_sftp.py b/tests/http/test_51_sftp.py index 052c7ef46980..acf76ef68356 100644 --- a/tests/http/test_51_sftp.py +++ b/tests/http/test_51_sftp.py @@ -41,10 +41,10 @@ class TestSftp: @pytest.fixture(autouse=True, scope='class') def _class_scope(self, env, sshd): - env.make_data_file(indir=sshd.home_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=sshd.home_dir, fname="data-10m", fsize=10*1024*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10*1024) - env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024) + env.make_data_file(indir=sshd.home_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=sshd.home_dir, fname="data-10m", fsize=10 * 1024 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10k", fsize=10 * 1024) + env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10 * 1024 * 1024) def test_51_01_insecure(self, env: Env, sshd: Sshd): curl = CurlClient(env=env) @@ -191,8 +191,10 @@ def check_downloads(self, client, srcfile: str, count: int, dfile = client.download_file(i) assert os.path.exists(dfile) if complete and not filecmp.cmp(srcfile, dfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(dfile).readlines(), + with open(srcfile) as fa, open(dfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=dfile, n=1)) @@ -202,8 +204,10 @@ def check_upload(self, sshd: Sshd, srcfile, destfile, binary=True): assert os.path.exists(srcfile) assert os.path.exists(destfile) if not filecmp.cmp(srcfile, destfile, shallow=False): - diff = "".join(difflib.unified_diff(a=open(srcfile).readlines(), - b=open(destfile).readlines(), + with open(srcfile) as fa, open(destfile) as fb: + a = fa.readlines() + b = fb.readlines() + diff = "".join(difflib.unified_diff(a=a, b=b, fromfile=srcfile, tofile=destfile, n=1)) diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py new file mode 100644 index 000000000000..9382ef70d9d2 --- /dev/null +++ b/tests/http/test_60_h3_proxy.py @@ -0,0 +1,626 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# *************************************************************************** +# _ _ ____ _ +# 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 +# +########################################################################### +# +import os +import subprocess +import time + +import pytest +from testenv import CurlClient, Env + +MARK_NEEDS_HTTPS_PROXY = pytest.mark.skipif( + condition=not Env.curl_has_feature("HTTPS-proxy"), + reason="curl lacks HTTPS-proxy support" +) +MARK_NEEDS_HTTP3 = pytest.mark.skipif( + condition=not Env.curl_has_feature("HTTP3"), reason="curl lacks HTTP/3 support" +) +MARK_NEEDS_PROXY_HTTP3 = pytest.mark.skipif( + condition=not Env.curl_has_feature("proxy-HTTP3"), + reason="curl lacks experimental HTTP/3 proxy support" +) +MARK_NEEDS_NGHTTP3 = pytest.mark.skipif( + condition=not Env.curl_uses_lib("nghttp3"), reason="only supported with nghttp3" +) +MARK_NEEDS_NGHTTP2 = pytest.mark.skipif( + condition=not Env.curl_uses_lib("nghttp2"), reason="only supported with nghttp2" +) +MARK_NEEDS_H2O = pytest.mark.skipif( + condition=not Env.have_h2o(), reason="no h2o available" +) +MARK_NEEDS_NGHTTPX = pytest.mark.skipif( + condition=not Env.have_nghttpx(), reason="no nghttpx available" +) + +UNSUPPORTED_OPT_MSG = "does not support this" +H2O_HELLO_MSG = '"message": "Hello from h2o HTTP/3 server"' + + +def _require_available(**items): + missing = [name for name, value in items.items() if not value] + if missing: + pytest.skip(f"{' or '.join(missing)} not available") + + +def _download_path(curl: CurlClient) -> str: + return os.path.join(curl.run_dir, "download_#1.data") + + +def _check_download_message(curl: CurlClient, expected: str): + dpath = _download_path(curl) + assert os.path.exists(dpath), f"Download file not found: {dpath}" + with open(dpath, "r") as fd: + content = fd.read() + assert expected in content, f"Unexpected response content: {content}" + + +def _check_download_size(curl: CurlClient, expected_size: int): + dpath = _download_path(curl) + assert os.path.exists(dpath), f"Download file not found: {dpath}" + actual = os.path.getsize(dpath) + assert actual == expected_size, f"expected {expected_size}B download, got {actual}B" + + +def _nghttpx_proxy_args( + env: Env, + nghttpx, + nghttpx_fwd, + proxy_proto: str, + tunnel: bool, +): + port = env.pts_port(proxy_proto) + domain = env.proxy_domain + xxarg = None + if proxy_proto == "h3": + port = nghttpx.port + domain = env.domain1 + xxarg = "--proxy-http3" + elif proxy_proto == "h2": + xxarg = "--proxy-http2" + + xargs = [ + "--proxy", f"https://{domain}:{port}/", + "--resolve", f"{domain}:{port}:127.0.0.1", + "--proxy-cacert", env.ca.cert_file + ] + if xxarg: + xargs.append(xxarg) + if tunnel: + xargs.append("--proxytunnel") + return xargs + + +def _h2o_proxy_args( + env: Env, + h2o_proxy, + proxy_proto: str, + tunnel: bool, +): + pport = env.pts_port(proxy_proto, use_h2o=True) + xargs = [ + "--proxy", f"https://{env.proxy_domain}:{pport}/", + "--resolve", f"{env.proxy_domain}:{pport}:127.0.0.1", + "--proxy-cacert", env.ca.cert_file, + "--cacert", env.ca.cert_file, + ] + if proxy_proto == "h2": + xargs.append("--proxy-http2") + elif proxy_proto == "h3": + xargs.append("--proxy-http3") + + if tunnel: + xargs.append("--proxytunnel") + + return xargs + + +@MARK_NEEDS_HTTPS_PROXY +@MARK_NEEDS_HTTP3 +@MARK_NEEDS_NGHTTP3 +class TestH3Proxy: + + # Success matrix for HTTP/3 proxy CONNECT / CONNECT-UDP. + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + @pytest.mark.parametrize( + ["alpn_proto", "proxy_proto"], + [ + pytest.param("http/1.1", "h3", id="h1_over_h3_proxytunnel"), + pytest.param( + "h2", + "h3", + marks=MARK_NEEDS_NGHTTP2, + id="h2_over_h3_proxytunnel", + ), + pytest.param("h3", "h3", id="h3_over_h3_proxytunnel"), + pytest.param( + "h3", + "h2", + marks=MARK_NEEDS_NGHTTP2, + id="h3_over_h2_proxytunnel", + ), + pytest.param("h3", "http/1.1", id="h3_over_h1_proxytunnel"), + ], + ) + def test_60_01_connect_tunnel( + self, + env: Env, + h2o_server, + h2o_proxy, + alpn_proto, + proxy_proto, + ): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args( + env, h2o_proxy, proxy_proto, tunnel=True + ) + + r = curl.http_download( + urls=[url], alpn_proto=alpn_proto, with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + _check_download_message(curl, H2O_HELLO_MSG) + + # Failure matrix when proxy side does not support requested mode. + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_NGHTTPX + @pytest.mark.parametrize( + ["alpn_proto", "proxy_proto", "exp_err"], + [ + pytest.param( + "http/1.1", + "h3", + "could not connect to server", + id="fail_h1_over_h3_proxytunnel", + ), + pytest.param( + "h2", + "h3", + "could not connect to server", + marks=MARK_NEEDS_NGHTTP2, + id="fail_h2_over_h3_proxytunnel", + ), + pytest.param( + "h3", + "h3", + "could not connect to server", + id="fail_h3_over_h3_proxytunnel", + ), + #pytest.param( + # "h3", + # "h2", + # "proxy closed connection", + # marks=MARK_NEEDS_NGHTTP2, + # id="fail_h3_over_h2_proxytunnel", + #), + pytest.param( + "h3", + "http/1.1", + "connect-udp tunnel failed", + id="fail_h3_over_h1_proxytunnel", + ), + ], + ) + def test_60_02_connect_tunnel_fail( + self, + env: Env, + httpd, + nghttpx, + nghttpx_fwd, + h2o_proxy, + alpn_proto, + proxy_proto, + exp_err, + ): + _require_available(httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd, + h2o_proxy=h2o_proxy) + + curl = CurlClient(env=env) + url = f"https://localhost:{env.https_port}/data.json" + proxy_args = _nghttpx_proxy_args( + env, nghttpx, nghttpx_fwd, proxy_proto, tunnel=True + ) + r = curl.http_download( + urls=[url], alpn_proto=alpn_proto, with_stats=True, extra_args=proxy_args + ) + assert r.exit_code != 0, f"Expected failure but curl succeeded: {r.dump_logs()}" + assert exp_err in r.stderr.lower(), ( + f"Expected protocol/proxy error but got: {r.dump_logs()}" + ) + + # Behavior checks for tunnel vs non-tunnel proxy mode selection. + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_NGHTTPX + @pytest.mark.parametrize( + ["proxy_proto"], + [ + #pytest.param("h3", id="proxy_h3"), + pytest.param("h2", marks=MARK_NEEDS_NGHTTP2, id="proxy_h2"), + pytest.param("http/1.1", id="proxy_h1"), + ], + ) + def test_60_03_h3_target_auto_connect_udp( + self, env: Env, httpd, nghttpx, nghttpx_fwd, proxy_proto + ): + _require_available( + httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd + ) + + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/data.json" + proxy_args = _nghttpx_proxy_args( + env, nghttpx, nghttpx_fwd, proxy_proto, tunnel=False + ) + r = curl.http_download( + urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args + ) + + # An HTTP/3 target auto-triggers CONNECT-UDP even without --proxytunnel, + # as HTTPS targets auto-trigger CONNECT. nghttpx does not support + # CONNECT-UDP so this fails, which confirms auto-CONNECT-UDP is active. + assert r.exit_code != 0, ( + "expected failure: h3 target auto-triggers CONNECT-UDP " + "which nghttpx does not support" + ) + assert "connect-udp" in r.stderr.lower(), ( + f"expected CONNECT-UDP attempt in output, got: {r.dump_logs()}" + ) + + # Guard checks for unsupported HTTP/3 proxy options. + + @pytest.mark.skipif( + condition=Env.curl_has_feature("proxy-HTTP3"), reason="curl has h3 proxy support" + ) + def test_60_04_guard_proxy_http3_unsupported(self, env: Env, httpd): + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/data.json" + proxy_args = [ + "--proxy", + "https://127.0.0.1:1/", + "--proxy-http3", + "--proxytunnel", + "--cacert", + env.ca.cert_file, + ] + + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_exit_code(2) + assert UNSUPPORTED_OPT_MSG in r.stderr.lower(), ( + f"Expected unsupported option failure but got: {r.stderr}" + ) + + # Robustness checks for shutdown and proxy loss during transfer. + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_05_graceful_shutdown(self, env: Env, h2o_server, h2o_proxy): + if not env.curl_is_debug(): + pytest.skip("needs debug curl for shutdown trace lines") + if not env.curl_is_verbose(): + pytest.skip("needs verbose-strings curl build") + + curl = CurlClient(env=env, run_env={"CURL_DEBUG": "all"}) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = curl.get_proxy_args(proto="h3", tunnel=True) + proxy_args.extend(["--cacert", env.ca.cert_file, "--insecure"]) + + r = curl.http_download( + urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + + shutdown_lines = [ + line + for line in r.trace_lines + if ("start shutdown(" in line.lower()) + or ("shutdown completely sent off" in line.lower()) + ] + assert shutdown_lines, f"No shutdown trace lines found:\n{r.stderr}" + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_06_proxy_drop_mid_transfer(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + + env.make_data_file(indir=h2o_server.docs_dir, fname="proxy-drop-20m", fsize=20 * 1024 * 1024) + proxy_port = h2o_proxy.port + url = f"https://localhost:{h2o_server.port}/proxy-drop-20m" + out_path = os.path.join(env.gen_dir, "proxy-drop.out") + if os.path.exists(out_path): + os.remove(out_path) + args = [ + env.curl, + "--http1.1", + "--proxy", f"https://{env.proxy_domain}:{proxy_port}/", + "--resolve", f"{env.proxy_domain}:{proxy_port}:127.0.0.1", + "--proxy-http3", + "--proxytunnel", + "--proxy-cacert", env.ca.cert_file, + "--cacert", env.ca.cert_file, + "--limit-rate", "10k", + "--max-time", "20", + "-o", out_path, + "-v", + url, + ] + + proc = None + try: + proc = subprocess.Popen( + args=args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) + while not os.path.exists(out_path): + time.sleep(0.1) + assert h2o_proxy.kill(), "failed to stop h2o proxy" + _, stderr = proc.communicate(timeout=30) + assert proc.returncode != 0, ( + "curl should fail when proxy is terminated mid-transfer" + ) + assert proc.returncode == 56, f'{stderr}' + finally: + if proc and (proc.poll() is None): + proc.kill() + proc.wait(timeout=5) + assert h2o_proxy.start(), "failed to restart h2o proxy" + + # Large file transfers and multiplexing through HTTP/3 proxy. + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_07_large_download(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + env.make_data_file(indir=h2o_server.docs_dir, fname="download-10m", fsize=10 * 1024 * 1024) + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/download-10m" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + _check_download_size(curl, 10 * 1024 * 1024) + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_08_large_upload(self, env: Env, httpd, h2o_server, h2o_proxy): + _require_available(h2o_proxy=h2o_proxy) + env.make_data_file(indir=env.gen_dir, fname="upload-2m", fsize=2 * 1024 * 1024) + fdata = os.path.join(env.gen_dir, "upload-2m") + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/curltest/echo?id=[0-0]" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + r = curl.http_upload( + urls=[url], + data=f"@{fdata}", + alpn_proto="http/1.1", + with_stats=True, + extra_args=proxy_args, + ) + r.check_response(count=1, http_status=200) + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_09_parallel_downloads(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + env.make_data_file(indir=h2o_server.docs_dir, fname="download-1m", fsize=1 * 1024 * 1024) + count = 5 + curl = CurlClient(env=env) + urln = f"https://localhost:{h2o_server.port}/download-1m?[0-{count - 1}]" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + proxy_args.extend(["--parallel", "--parallel-max", f"{count}"]) + r = curl.http_download( + urls=[urln], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=count, http_status=200) + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_10_proxy_basic_auth(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + proxy_args.extend(["--proxy-user", "testuser:testpass"]) + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + _check_download_message(curl, H2O_HELLO_MSG) + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_11_connection_reuse(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + urln = f"https://localhost:{h2o_server.port}/data.json?[0-2]" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + r = curl.http_download( + urls=[urln], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=3, http_status=200) + assert r.total_connects <= 3, ( + f"expected proxy connection reuse, got {r.total_connects} connects" + ) + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + @pytest.mark.skipif(condition=not Env.curl_has_feature('SSLS-EXPORT'), + reason='curl lacks SSL session export support') + def test_60_12_quic_session_resumption(self, env: Env, h2o_server, h2o_proxy): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/data.json" + xargs = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + session_file = os.path.join(env.gen_dir, 'test_60_12.sessions') + if os.path.exists(session_file): + os.remove(session_file) + xargs.extend(['--ssl-sessions', session_file]) + # First request establishes QUIC session + r1 = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=xargs + ) + r1.check_response(count=1, http_status=200) + xargs.extend(['--trace-config', 'ssls']) + r2 = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=xargs + ) + r2.check_response(count=1, http_status=200) + reuses = [line for line in r2.trace_lines if '[SSLS] took session for proxy.http.curl.se' in line] + assert len(reuses), f'{r2.dump_logs()}' + + # CONNECT-UDP tunnel payload size and capsule-protocol tests. + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + @pytest.mark.parametrize( + "fname,fsize", + [ + ("download-1400", 1400), + ("download-1m", 1 * 1024 * 1024), + ("download-10m", 10 * 1024 * 1024), + ], + ) + def test_60_13_udp_tunnel_payload_sizes( + self, env: Env, h2o_server, h2o_proxy, fname, fsize + ): + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + env.make_data_file(indir=h2o_server.docs_dir, fname=fname, fsize=fsize) + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/{fname}" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + r = curl.http_download( + urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + _check_download_size(curl, fsize) + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_NGHTTPX + def test_60_14_udp_tunnel_capsule_absent( + self, env: Env, httpd, nghttpx, nghttpx_fwd + ): + _require_available( + httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd + ) + curl = CurlClient(env=env) + url = f"https://localhost:{httpd.ports['https']}/data.json" + proxy_args = _nghttpx_proxy_args( + env, nghttpx, nghttpx_fwd, "h3", tunnel=True + ) + r = curl.http_download( + urls=[url], alpn_proto="h3", with_stats=True, extra_args=proxy_args + ) + assert r.exit_code != 0, ( + "expected failure: nghttpx does not support CONNECT-UDP / Capsule-Protocol" + ) + + # Timeout and protocol-mismatch edge cases. + + #@MARK_NEEDS_PROXY_HTTP3 + #@MARK_NEEDS_H2O + #def test_60_15_connect_timeout(self, env: Env, h2o_proxy): + # _require_available(h2o_proxy=h2o_proxy) + # curl = CurlClient(env=env, timeout=15) + # url = f"https://localhost:{h2o_proxy.port}/data.json" + # # ipv6 0100::/64 is supposed to go into the void (rfc6666) + # xargs = [ + # '--proxy', 'https://xxx.invalid/', + # '--resolve', 'xxx.invalid:443:0100::1,0100::2,0100::3', + # '--proxy-http3', '--proxytunnel', + # '--connect-timeout', '1', + # ] + # r = curl.http_download( + # urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=xargs + # ) + # r.check_exit_code(28) # CURLE_OPERATION_TIMEDOUT + # assert r.duration.total_seconds() < 10, ( + # f"timeout not respected: took {r.duration.total_seconds():.1f}s" + # ) + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + @MARK_NEEDS_NGHTTP2 + def test_60_16_h2_uses_connect_tcp_not_udp(self, env: Env, httpd, h2o_proxy): + _require_available(httpd=httpd, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env) + url = f"https://localhost:{env.https_port}/data.json" + proxy_args = curl.get_proxy_args("h3", tunnel=True) + # h2 inner traffic always uses CONNECT (TCP), never CONNECT-UDP, + # even through an HTTP/3 proxy with --proxytunnel. h2o supports + # CONNECT TCP tunneling, so this request succeeds. + r = curl.http_download( + urls=[url], alpn_proto="h2", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + + # Verify that happy eyeballs is active for HTTP/3 proxy connections. + # + # With the H3-PROXY filter sitting above HAPPY-EYEBALLS -> UDP, address + # family selection to the proxy is done by happy eyeballs. + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + def test_60_17_happy_eyeballs_filter_present(self, env: Env, h2o_server, h2o_proxy): + """Verbose trace confirms HAPPY-EYEBALLS filter is in the H3 proxy chain.""" + if not env.curl_is_debug(): + pytest.skip("needs debug curl for filter trace") + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + curl = CurlClient(env=env, run_env={"CURL_DEBUG": "HAPPY-EYEBALLS,H3-PROXY"}) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + r = curl.http_download( + urls=[url], alpn_proto="http/1.1", with_stats=True, extra_args=proxy_args + ) + r.check_response(count=1, http_status=200) + assert "happy-eyeballs" in r.stderr.lower(), ( + f"expected HAPPY-EYEBALLS trace for H3 proxy, got: {r.stderr}" + ) + + @MARK_NEEDS_PROXY_HTTP3 + @MARK_NEEDS_H2O + @MARK_NEEDS_NGHTTP2 + def test_60_18_happy_eyeballs_ipv4_all_proto(self, env: Env, h2o_server, h2o_proxy): + """IPv4-forced H3 proxy works for h1/h2/h3 inner protocols.""" + _require_available(h2o_server=h2o_server, h2o_proxy=h2o_proxy) + for alpn_proto in ["http/1.1", "h2", "h3"]: + curl = CurlClient(env=env) + url = f"https://localhost:{h2o_server.port}/data.json" + proxy_args = _h2o_proxy_args(env, h2o_proxy, "h3", tunnel=True) + proxy_args.append("--ipv4") + r = curl.http_download( + urls=[url], + alpn_proto=alpn_proto, + with_stats=True, + extra_args=proxy_args, + ) + r.check_response(count=1, http_status=200) diff --git a/tests/http/testenv/caddy.py b/tests/http/testenv/caddy.py index eece1a5a394a..1d554b1906f4 100644 --- a/tests/http/testenv/caddy.py +++ b/tests/http/testenv/caddy.py @@ -55,6 +55,7 @@ def __init__(self, env: Env): self._conf_file = os.path.join(self._caddy_dir, 'Caddyfile') self._error_log = os.path.join(self._caddy_dir, 'caddy.log') self._tmp_dir = os.path.join(self._caddy_dir, 'tmp') + self._error_fd = None self._process = None self._http_port = 0 self._https_port = 0 @@ -68,6 +69,11 @@ def docs_dir(self): def port(self) -> int: return self._https_port + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) @@ -107,8 +113,8 @@ def start(self, wait_live=True): args = [ self._caddy, 'run' ] - caddyerr = open(self._error_log, 'a') - self._process = subprocess.Popen(args=args, cwd=self._caddy_dir, stderr=caddyerr) + self._error_fd = open(self._error_log, 'a') + self._process = subprocess.Popen(args=args, cwd=self._caddy_dir, stderr=self._error_fd) if self._process.returncode is not None: return False return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) @@ -122,7 +128,9 @@ def stop(self, wait_dead=True): except Exception: self._process.kill() self._process = None + self.close_log() return not wait_dead or self.wait_dead(timeout=timedelta(seconds=5)) + self.close_log() return True def restart(self): diff --git a/tests/http/testenv/certs.py b/tests/http/testenv/certs.py index 3e206efcdc80..85012634a5a7 100644 --- a/tests/http/testenv/certs.py +++ b/tests/http/testenv/certs.py @@ -484,8 +484,7 @@ def _add_leaf_usages(csr: Any, domains: List[str], issuer: Credentials) -> Any: else: try: names.append(x509.IPAddress(ipaddress.ip_address(name))) - # TODO: specify specific exceptions here - except: # noqa: E722 + except ValueError: names.append(x509.DNSName(name)) return csr.add_extension( diff --git a/tests/http/testenv/client.py b/tests/http/testenv/client.py index 36ac8a599e6d..76c4822cbc8d 100644 --- a/tests/http/testenv/client.py +++ b/tests/http/testenv/client.py @@ -104,8 +104,9 @@ def run(self, args): log.warning(f'Timeout after {self._timeout}s: {args}') exitcode = -1 exception = 'TimeoutExpired' - coutput = open(self._stdoutfile).readlines() - cerrput = open(self._stderrfile).readlines() + with open(self._stdoutfile) as fout, open(self._stderrfile) as ferr: + coutput = fout.readlines() + cerrput = ferr.readlines() return ExecResult(args=myargs, exit_code=exitcode, exception=exception, stdout=coutput, stderr=cerrput, duration=datetime.now() - start) @@ -113,8 +114,10 @@ def run(self, args): def dump_logs(self): lines = [] lines.append('>>--stdout ----------------------------------------------\n') - lines.extend(open(self._stdoutfile).readlines()) + with open(self._stdoutfile) as fd: + lines.extend(fd.readlines()) lines.append('>>--stderr ----------------------------------------------\n') - lines.extend(open(self._stderrfile).readlines()) + with open(self._stderrfile) as fd: + lines.extend(fd.readlines()) lines.append('<<-------------------------------------------------------\n') return ''.join(lines) diff --git a/tests/http/testenv/curl.py b/tests/http/testenv/curl.py index 99aa649bc0fd..3071d7dd4aa9 100644 --- a/tests/http/testenv/curl.py +++ b/tests/http/testenv/curl.py @@ -89,6 +89,7 @@ def sample(self): 'rss': mem.rss, }) except psutil.NoSuchProcess: + # process may exit between sampling ticks: ignore this pass def finish(self): @@ -187,17 +188,18 @@ def __init__(self, env, run_dir): self._stdoutfile = os.path.join(self._run_dir, 'tcpdump.out') self._stderrfile = os.path.join(self._run_dir, 'tcpdump.err') - def get_rsts(self, ports: List[int]|None = None) -> Optional[List[str]]: + def get_rsts(self, ports: Optional[List[int]] = None) -> Optional[List[str]]: if self._proc: raise Exception('tcpdump still running') lines = [] - for line in open(self._stdoutfile): - m = re.match(r'.* IP 127\.0\.0\.1\.(\d+) [<>] 127\.0\.0\.1\.(\d+):.*', line) - if m: - sport = int(m.group(1)) - dport = int(m.group(2)) - if ports is None or sport in ports or dport in ports: - lines.append(line) + with open(self._stdoutfile) as fd: + for line in fd: + m = re.match(r'.* IP 127\.0\.0\.1\.(\d+) [<>] 127\.0\.0\.1\.(\d+):.*', line) + if m: + sport = int(m.group(1)) + dport = int(m.group(2)) + if ports is None or sport in ports or dport in ports: + lines.append(line) return lines @property @@ -208,7 +210,8 @@ def stats(self) -> Optional[List[str]]: def stderr(self) -> List[str]: if self._proc: raise Exception('tcpdump still running') - return open(self._stderrfile).readlines() + with open(self._stderrfile) as fd: + return fd.readlines() def sample(self): # not sure how to make that detection reliable for all platforms @@ -236,6 +239,7 @@ def sample(self): try: self._proc.wait(timeout=1) except subprocess.TimeoutExpired: + # timeout means tcpdump is still running pass except Exception: log.exception('Tcpdump') @@ -280,11 +284,11 @@ def __init__(self, args: List[str], exit_code: int, if with_stats: self._parse_stats() else: - # noinspection PyBroadException try: out = ''.join(self._stdout) self._json_out = json.loads(out) - except: # noqa: E722 + except (json.JSONDecodeError, TypeError, ValueError): + # stdout not guaranteed to be JSON, keep _json_out as None pass def __repr__(self): @@ -296,8 +300,7 @@ def _parse_stats(self): for line in self._stdout: try: self._stats.append(json.loads(line)) - # TODO: specify specific exceptions here - except: # noqa: E722 + except (json.JSONDecodeError, TypeError, ValueError): log.exception(f'not a JSON stat: {line}') break @@ -393,7 +396,7 @@ def check_exit_code(self, code: Union[int, bool]): f'got {self.exit_code}\n{self.dump_logs()}' elif code is False: assert self.exit_code != 0, f'expected exit code {code}, '\ - f'got {self.exit_code}\n{self.dump_logs()}' + f'got {self.exit_code}\n{self.dump_logs()}' else: assert self.exit_code == code, f'expected exit code {code}, '\ f'got {self.exit_code}\n{self.dump_logs()}' @@ -540,23 +543,8 @@ def check_stats_timeline(self, idx): ref_tl += ['time_namelookup', 'time_connect'] if url.startswith('https:'): ref_tl += ['time_appconnect'] - # what kind of transfer was it? - if s['size_upload'] == 0 and s['size_download'] > 0: - # this is a download - dl_tl = ['time_pretransfer'] - if s['size_request'] > 0: - dl_tl = ['time_posttransfer'] + dl_tl - ref_tl += dl_tl - # the first byte of the response may arrive before we - # track the other times when the client is slow (CI). - somewhere_keys.extend(['time_starttransfer']) - elif s['size_upload'] > 0 and s['size_download'] == 0: - # this is an upload - ul_tl = ['time_pretransfer', 'time_posttransfer'] - ref_tl += ul_tl - else: - # could be a 0-length upload or 0-length download, not sure - exact_match = False + ref_tl += ['time_pretransfer', 'time_posttransfer'] + somewhere_keys.extend(['time_starttransfer']) # always there at the end ref_tl += ['time_total'] @@ -684,11 +672,17 @@ def _mkpath(self, path): def get_proxy_args(self, proto: str = 'http/1.1', proxys: bool = True, tunnel: bool = False, - use_ip: bool = False, use_ipv6: bool = False): + use_ip: bool = False, use_ipv6: bool = False, + use_h2o: bool = False): proxy_name = '[::1]' if use_ipv6 else \ self._server_addr if use_ip else self.env.proxy_domain if proxys: - pport = self.env.pts_port(proto) if tunnel else self.env.proxys_port + if tunnel: + pport = self.env.pts_port(proto, use_h2o=use_h2o) + elif proto == 'h3': + pport = self.env.h3proxys_port + else: + pport = self.env.proxys_port xargs = [ '--proxy', f'https://{proxy_name}:{pport}/', '--proxy-cacert', self.env.ca.cert_file, @@ -697,6 +691,8 @@ def get_proxy_args(self, proto: str = 'http/1.1', xargs.extend(['--resolve', f'{proxy_name}:{pport}:{self._server_addr}']) if proto == 'h2': xargs.append('--proxy-http2') + elif proto == 'h3': + xargs.append('--proxy-http3') else: xargs = [ '--proxy', f'http://{proxy_name}:{self.env.proxy_port}/', @@ -729,7 +725,7 @@ def http_download(self, urls: List[str], no_save: bool = False, limit_rate: Optional[str] = None, extra_args: Optional[List[str]] = None, - url_options: Optional[Dict[str,List[str]]] = None): + url_options: Optional[Dict[str, List[str]]] = None): if extra_args is None: extra_args = [] if no_save: @@ -1034,8 +1030,6 @@ def _run(self, args, intext='', with_stats: bool = False, cwd=self._run_dir, shell=False, env=self._run_env) profile = RunProfile(p.pid, started_at, self._run_dir) - if intext is not None and False: - p.communicate(input=intext.encode(), timeout=1) if self._with_perf: perf = PerfProfile(p.pid, self._run_dir) perf.start() @@ -1079,8 +1073,9 @@ def _run(self, args, intext='', with_stats: bool = False, dtrace.finish() if self._with_flame: self._generate_flame(args, dtrace=dtrace, perf=perf) - coutput = open(self._stdoutfile).readlines() - cerrput = open(self._stderrfile).readlines() + with open(self._stdoutfile) as fout, open(self._stderrfile) as ferr: + coutput = fout.readlines() + cerrput = ferr.readlines() return ExecResult(args=args, exit_code=exitcode, exception=exception, stdout=coutput, stderr=cerrput, duration=ended_at - started_at, @@ -1119,7 +1114,7 @@ def _complete_args(self, urls, timeout=None, options=None, else: force_resolve = self._force_resolv - args = [self._curl, "-s", "--path-as-is"] + args = [self._curl, "--disable", "-s", "--path-as-is"] if 'CURL_TEST_EVENT' in os.environ: args.append('--test-event') @@ -1174,7 +1169,8 @@ def _complete_args(self, urls, timeout=None, options=None, return args def _parse_headerfile(self, headerfile: str, r: Optional[ExecResult] = None) -> ExecResult: - lines = open(headerfile).readlines() + with open(headerfile) as fd: + lines = fd.readlines() if r is None: r = ExecResult(args=[], exit_code=0, stdout=[], stderr=[]) @@ -1231,7 +1227,7 @@ def fin_response(resp): def _perf_collapse(self, perf: PerfProfile, file_err): if not os.path.exists(perf.file): - raise Exception(f'dtrace output file does not exist: {perf.file}') + raise Exception(f'perf output file does not exist: {perf.file}') fg_collapse = os.path.join(self._fg_dir, 'stackcollapse-perf.pl') if not os.path.exists(fg_collapse): raise Exception(f'FlameGraph script not found: {fg_collapse}') diff --git a/tests/http/testenv/dante.py b/tests/http/testenv/dante.py index 2feb42eb7cca..b2ea4dd0cfe4 100644 --- a/tests/http/testenv/dante.py +++ b/tests/http/testenv/dante.py @@ -57,6 +57,7 @@ def __init__(self, env: Env): self._dante_log = os.path.join(self._dante_dir, 'dante.log') self._error_log = os.path.join(self._dante_dir, 'error.log') self._pid_file = os.path.join(self._dante_dir, 'dante.pid') + self._error_fd = None self._process = None self.clear_logs() @@ -65,6 +66,11 @@ def __init__(self, env: Env): def port(self) -> int: return self._port + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) self._rmf(self._dante_log) @@ -89,7 +95,7 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None - return not wait_dead or True + self.close_log() return True def restart(self): @@ -122,8 +128,8 @@ def start(self, wait_live=True): '-p', f'{self._pid_file}', '-d', '0', ] - procerr = open(self._error_log, 'a') - self._process = subprocess.Popen(args=args, stderr=procerr) + self._error_fd = open(self._error_log, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/dnsd.py b/tests/http/testenv/dnsd.py index b8ff097280a3..a7dc0885571f 100644 --- a/tests/http/testenv/dnsd.py +++ b/tests/http/testenv/dnsd.py @@ -56,6 +56,7 @@ def __init__(self, env: Env): self._conf_file = os.path.join(self._log_dir, 'dnsd.cmd') self._pid_file = os.path.join(self._log_dir, 'dnsd.pid') self._error_log = os.path.join(self._log_dir, 'dnsd.err.log') + self._error_fd = None self._process = None self.clear_logs() @@ -64,6 +65,11 @@ def __init__(self, env: Env): def port(self) -> int: return self._port + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._log_file) self._rmf(self._error_log) @@ -87,7 +93,7 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None - return not wait_dead or True + self.close_log() return True def restart(self): @@ -122,8 +128,8 @@ def start(self, wait_live=True): '--logfile', f'{self._log_file}', '--pidfile', f'{self._pid_file}', ] - procerr = open(self._error_log, 'a') - self._process = subprocess.Popen(args=args, stderr=procerr) + self._error_fd = open(self._error_log, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/env.py b/tests/http/testenv/env.py index 30fc0d0a734a..4a18d65ffab6 100644 --- a/tests/http/testenv/env.py +++ b/tests/http/testenv/env.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -#*************************************************************************** +# *************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | # / __| | | | |_) | | @@ -54,20 +54,21 @@ def init_config_from(conf_path): TESTS_HTTPD_PATH = os.path.dirname(os.path.dirname(__file__)) PROJ_PATH = os.path.dirname(os.path.dirname(TESTS_HTTPD_PATH)) TOP_PATH = os.path.join(os.getcwd(), os.path.pardir) -CONFIG_PATH = os.path.join(TOP_PATH, 'tests', 'http', 'config.ini') +CONFIG_PATH = os.path.join(TOP_PATH, "tests", "http", "config.ini") if not os.path.exists(CONFIG_PATH): - ALT_CONFIG_PATH = os.path.join(PROJ_PATH, 'tests', 'http', 'config.ini') + ALT_CONFIG_PATH = os.path.join(PROJ_PATH, "tests", "http", "config.ini") if not os.path.exists(ALT_CONFIG_PATH): - raise Exception(f'unable to find config.ini in {CONFIG_PATH} nor {ALT_CONFIG_PATH}') + raise Exception( + f"unable to find config.ini in {CONFIG_PATH} nor {ALT_CONFIG_PATH}" + ) TOP_PATH = PROJ_PATH CONFIG_PATH = ALT_CONFIG_PATH DEF_CONFIG = init_config_from(CONFIG_PATH) -CURL = os.path.join(TOP_PATH, 'src', 'curl') -CURLINFO = os.path.join(TOP_PATH, 'src', 'curlinfo') +CURL = os.path.join(TOP_PATH, "src", "curl") +CURLINFO = os.path.join(TOP_PATH, "src", "curlinfo") class NghttpxUtil: - CMD = None VERSION_FULL = None @@ -76,34 +77,37 @@ def version(cls, cmd): if cmd is None: return None if cls.VERSION_FULL is None or cmd != cls.CMD: - p = subprocess.run(args=[cmd, '--version'], - capture_output=True, text=True) + p = subprocess.run(args=[cmd, "--version"], capture_output=True, text=True) if p.returncode != 0: - raise RuntimeError(f'{cmd} --version failed with exit code: {p.returncode}') + raise RuntimeError( + f"{cmd} --version failed with exit code: {p.returncode}" + ) cls.CMD = cmd for line in p.stdout.splitlines(keepends=False): - if line.startswith('nghttpx '): + if line.startswith("nghttpx "): cls.VERSION_FULL = line if cls.VERSION_FULL is None: - raise RuntimeError(f'{cmd}: unable to determine version') + raise RuntimeError(f"{cmd}: unable to determine version") return cls.VERSION_FULL @staticmethod def version_with_h3(version): - return re.match(r'.* ngtcp2/\d+\.\d+\.\d+.*', version) is not None + return re.match(r".* ngtcp2/\d+\.\d+\.\d+.*", version) is not None class EnvConfig: - - def __init__(self, pytestconfig: Optional[pytest.Config] = None, - testrun_uid=None, - worker_id=None): + def __init__( + self, + pytestconfig: Optional[pytest.Config] = None, + testrun_uid=None, + worker_id=None, + ): self.pytestconfig = pytestconfig self.testrun_uid = testrun_uid - self.worker_id = worker_id if worker_id is not None else 'master' + self.worker_id = worker_id if worker_id is not None else "master" self.tests_dir = TESTS_HTTPD_PATH - self.gen_root = self.gen_dir = os.path.join(self.tests_dir, 'gen') - if self.worker_id != 'master': + self.gen_root = self.gen_dir = os.path.join(self.tests_dir, "gen") + if self.worker_id != "master": self.gen_dir = os.path.join(self.gen_dir, self.worker_id) self.project_dir = os.path.dirname(os.path.dirname(self.tests_dir)) self.build_dir = TOP_PATH @@ -111,57 +115,56 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, # check cur and its features self.curl = CURL self.curlinfo = CURLINFO - if 'CURL' in os.environ: - self.curl = os.environ['CURL'] + if "CURL" in os.environ: + self.curl = os.environ["CURL"] self.curl_props = { - 'version_string': '', - 'version': '', - 'os': '', - 'fullname': '', - 'features_string': '', - 'features': set(), - 'protocols_string': '', - 'protocols': set(), - 'libs': set(), - 'lib_versions': set(), + "version_string": "", + "version": "", + "os": "", + "fullname": "", + "features_string": "", + "features": set(), + "protocols_string": "", + "protocols": set(), + "libs": set(), + "lib_versions": set(), } self.curl_is_debug = False self.curl_protos = [] - p = subprocess.run(args=[self.curl, '-V'], - capture_output=True, text=True) + p = subprocess.run(args=[self.curl, "-V"], capture_output=True, text=True) if p.returncode != 0: - raise RuntimeError(f'{self.curl} -V failed with exit code: {p.returncode}') - if p.stderr.startswith('WARNING:'): + raise RuntimeError(f"{self.curl} -V failed with exit code: {p.returncode}") + if p.stderr.startswith("WARNING:"): self.curl_is_debug = True for line in p.stdout.splitlines(keepends=False): - if line.startswith('curl '): - self.curl_props['version_string'] = line - m = re.match(r'^curl (?P\S+) (?P\S+) (?P.*)$', line) + if line.startswith("curl "): + self.curl_props["version_string"] = line + m = re.match(r"^curl (?P\S+) (?P\S+) (?P.*)$", line) if m: - self.curl_props['fullname'] = m.group(0) - self.curl_props['version'] = m.group('version') - self.curl_props['os'] = m.group('os') - self.curl_props['lib_versions'] = { - lib.lower() for lib in m.group('libs').split(' ') + self.curl_props["fullname"] = m.group(0) + self.curl_props["version"] = m.group("version") + self.curl_props["os"] = m.group("os") + self.curl_props["lib_versions"] = { + lib.lower() for lib in m.group("libs").split(" ") } - self.curl_props['libs'] = { - re.sub(r'/[a-z0-9.-]*', '', lib) for lib in self.curl_props['lib_versions'] + self.curl_props["libs"] = { + re.sub(r"/[a-z0-9.-]*", "", lib) + for lib in self.curl_props["lib_versions"] } - if line.startswith('Features: '): - self.curl_props['features_string'] = line[10:] - self.curl_props['features'] = { - feat.lower() for feat in line[10:].split(' ') + if line.startswith("Features: "): + self.curl_props["features_string"] = line[10:] + self.curl_props["features"] = { + feat.lower() for feat in line[10:].split(" ") } - if line.startswith('Protocols: '): - self.curl_props['protocols_string'] = line[11:] - self.curl_props['protocols'] = { - prot.lower() for prot in line[11:].split(' ') + if line.startswith("Protocols: "): + self.curl_props["protocols_string"] = line[11:] + self.curl_props["protocols"] = { + prot.lower() for prot in line[11:].split(" ") } - p = subprocess.run(args=[self.curlinfo], - capture_output=True, text=True) + p = subprocess.run(args=[self.curlinfo], capture_output=True, text=True) if p.returncode != 0: - raise RuntimeError(f'{self.curlinfo} failed with exit code: {p.returncode}') + raise RuntimeError(f"{self.curlinfo} failed with exit code: {p.returncode}") self.curl_is_verbose = 'verbose-strings: ON' in p.stdout self.curl_can_cert_status = 'cert-status: ON' in p.stdout self.curl_override_dns = 'override-dns: ON' in p.stdout @@ -169,18 +172,18 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, self.ports = {} - self.httpd = self.config['httpd']['httpd'] - self.apxs = self.config['httpd']['apxs'] + self.httpd = self.config["httpd"]["httpd"] + self.apxs = self.config["httpd"]["apxs"] if len(self.apxs) == 0: self.apxs = None self._httpd_version = None self.examples_pem = { - 'key': 'xxx', - 'cert': 'xxx', + "key": "xxx", + "cert": "xxx", } - self.htdocs_dir = os.path.join(self.gen_dir, 'htdocs') - self.tld = 'http.curl.se' + self.htdocs_dir = os.path.join(self.gen_dir, "htdocs") + self.tld = "http.curl.se" self.domain1 = f"one.{self.tld}" self.domain1brotli = f"brotli.one.{self.tld}" self.domain2 = f"two.{self.tld}" @@ -188,22 +191,43 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, self.proxy_domain = f"proxy.{self.tld}" self.expired_domain = f"expired.{self.tld}" self.cert_specs = [ - CertificateSpec(domains=[self.domain1, self.domain1brotli, 'localhost', '127.0.0.1'], key_type='rsa2048'), - CertificateSpec(name='domain1-no-ip', domains=[self.domain1, self.domain1brotli], key_type='rsa2048'), - CertificateSpec(name='domain1-very-bad', domains=[self.domain1, 'dns:127.0.0.1'], key_type='rsa2048'), - CertificateSpec(domains=[self.domain2], key_type='rsa2048'), - CertificateSpec(domains=[self.ftp_domain], key_type='rsa2048'), - CertificateSpec(domains=[self.proxy_domain, '127.0.0.1'], key_type='rsa2048'), - CertificateSpec(domains=[self.expired_domain], key_type='rsa2048', - valid_from=timedelta(days=-100), valid_to=timedelta(days=-10)), - CertificateSpec(name="clientsX", sub_specs=[ - CertificateSpec(name="user1", client=True), - ]), + CertificateSpec( + domains=[self.domain1, self.domain1brotli, "localhost", "127.0.0.1"], + key_type="rsa2048", + ), + CertificateSpec( + name="domain1-no-ip", + domains=[self.domain1, self.domain1brotli], + key_type="rsa2048", + ), + CertificateSpec( + name="domain1-very-bad", + domains=[self.domain1, "dns:127.0.0.1"], + key_type="rsa2048", + ), + CertificateSpec(domains=[self.domain2], key_type="rsa2048"), + CertificateSpec(domains=[self.ftp_domain], key_type="rsa2048"), + CertificateSpec( + domains=[self.proxy_domain, "127.0.0.1"], key_type="rsa2048" + ), + CertificateSpec( + domains=[self.expired_domain], + key_type="rsa2048", + valid_from=timedelta(days=-100), + valid_to=timedelta(days=-10), + ), + CertificateSpec( + name="clientsX", + sub_specs=[ + CertificateSpec(name="user1", client=True), + ], + ), ] - self.openssl = 'openssl' - p = subprocess.run(args=[self.openssl, 'version'], - capture_output=True, text=True) + self.openssl = "openssl" + p = subprocess.run( + args=[self.openssl, "version"], capture_output=True, text=True + ) if p.returncode != 0: # no openssl in path self.openssl = None @@ -211,7 +235,7 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, else: self.openssl_version = p.stdout.strip() - self.nghttpx = self.config['nghttpx']['nghttpx'] + self.nghttpx = self.config["nghttpx"]["nghttpx"] if len(self.nghttpx.strip()) == 0: self.nghttpx = None self._nghttpx_version = None @@ -220,30 +244,58 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, self._nghttpx_version = NghttpxUtil.version(self.nghttpx) self.nghttpx_with_h3 = NghttpxUtil.version_with_h3(self._nghttpx_version) - self.caddy = self.config['caddy']['caddy'] + self.caddy = self.config["caddy"]["caddy"] self._caddy_version = None if len(self.caddy.strip()) == 0: self.caddy = None + + self.h2o = self.config["h2o"]["h2o"] + if len(self.h2o.strip()) == 0: + self.h2o = None + self._h2o_version = None + if self.h2o is not None: + try: + p = subprocess.run( + args=[self.h2o, "--version"], capture_output=True, text=True + ) + if p.returncode != 0: + # not a working h2o + self.h2o = None + else: + # h2o --version output format: "h2o version 2.3.0" + m = re.search(r"h2o version (\S+)", p.stdout) + if m: + self._h2o_version = m.group(1) + else: + self.h2o = None + except Exception: + log.exception("checking h2o version") + self.h2o = None + if self.caddy is not None: - p = subprocess.run(args=[self.caddy, 'version'], - capture_output=True, text=True) + p = subprocess.run( + args=[self.caddy, "version"], capture_output=True, text=True + ) if p.returncode != 0: # not a working caddy self.caddy = None - m = re.match(r'v?(\d+\.\d+\.\d+).*', p.stdout) + m = re.match(r"v?(\d+\.\d+\.\d+).*", p.stdout) if m: self._caddy_version = m.group(1) else: - raise RuntimeError(f'Unable to determine caddy version from: {p.stdout}') + raise RuntimeError( + f"Unable to determine caddy version from: {p.stdout}" + ) - self.vsftpd = self.config['vsftpd']['vsftpd'] - if self.vsftpd == '': + self.vsftpd = self.config["vsftpd"]["vsftpd"] + if self.vsftpd == "": self.vsftpd = None self._vsftpd_version = None if self.vsftpd is not None: - with tempfile.TemporaryFile('w+') as tmp: - p = subprocess.run(args=[self.vsftpd, '-v'], - capture_output=True, text=True, stdin=tmp) + with tempfile.TemporaryFile("w+") as tmp: + p = subprocess.run( + args=[self.vsftpd, "-v"], capture_output=True, text=True, stdin=tmp + ) if p.returncode != 0: # not a working vsftpd self.vsftpd = None @@ -256,80 +308,83 @@ def __init__(self, pytestconfig: Optional[pytest.Config] = None, # any data there instead. tmp.seek(0) ver_text = tmp.read() - m = re.match(r'vsftpd: version (\d+\.\d+\.\d+)', ver_text) + m = re.match(r"vsftpd: version (\d+\.\d+\.\d+)", ver_text) if m: self._vsftpd_version = m.group(1) elif len(p.stderr) == 0: # vsftp does not use stdout or stderr for printing its version... -.- - self._vsftpd_version = 'unknown' + self._vsftpd_version = "unknown" else: - raise Exception(f'Unable to determine VsFTPD version from: {p.stderr}') + raise Exception(f"Unable to determine VsFTPD version from: {p.stderr}") - self.danted = self.config['danted']['danted'] - if self.danted == '': + self.danted = self.config["danted"]["danted"] + if self.danted == "": self.danted = None self._danted_version = None if self.danted is not None: - p = subprocess.run(args=[self.danted, '-v'], - capture_output=True, text=True) + p = subprocess.run(args=[self.danted, "-v"], capture_output=True, text=True) assert p.returncode == 0 if p.returncode != 0: # not a working vsftpd self.danted = None - m = re.match(r'^Dante v(\d+\.\d+\.\d+).*', p.stdout) + m = re.match(r"^Dante v(\d+\.\d+\.\d+).*", p.stdout) if not m: - m = re.match(r'^Dante v(\d+\.\d+\.\d+).*', p.stderr) + m = re.match(r"^Dante v(\d+\.\d+\.\d+).*", p.stderr) if m: self._danted_version = m.group(1) else: self.danted = None - raise Exception(f'Unable to determine danted version from: {p.stderr}') + raise Exception(f"Unable to determine danted version from: {p.stderr}") - self.sshd = self.config['sshd']['sshd'] - if self.sshd == '': + self.sshd = self.config["sshd"]["sshd"] + if self.sshd == "": self.sshd = None self._sshd_version = None if self.sshd is not None: - p = subprocess.run(args=[self.sshd, '-V'], - capture_output=True, text=True) + p = subprocess.run(args=[self.sshd, "-V"], capture_output=True, text=True) assert p.returncode == 0 if p.returncode != 0: self.sshd = None else: - m = re.match(r'^OpenSSH_(\d+\.\d+.*),.*', p.stderr) - assert m, f'version: {p.stderr}' + m = re.match(r"^OpenSSH_(\d+\.\d+.*),.*", p.stderr) + assert m, f"version: {p.stderr}" if m: self._sshd_version = m.group(1) else: self.sshd = None - raise Exception(f'Unable to determine sshd version from: {p.stderr}') + raise Exception( + f"Unable to determine sshd version from: {p.stderr}" + ) if self.sshd: - self.sftpd = self.config['sshd']['sftpd'] - if self.sftpd == '': + self.sftpd = self.config["sshd"]["sftpd"] + if self.sftpd == "": self.sftpd = None else: self.sftpd = None - self._tcpdump = shutil.which('tcpdump') + self._tcpdump = shutil.which("tcpdump") @property def httpd_version(self): if self._httpd_version is None and self.apxs is not None: try: - p = subprocess.run(args=[self.apxs, '-q', 'HTTPD_VERSION'], - capture_output=True, text=True) + p = subprocess.run( + args=[self.apxs, "-q", "HTTPD_VERSION"], + capture_output=True, + text=True, + ) if p.returncode != 0: - log.error(f'{self.apxs} failed to query HTTPD_VERSION: {p}') + log.error(f"{self.apxs} failed to query HTTPD_VERSION: {p}") else: self._httpd_version = p.stdout.strip() except Exception: - log.exception(f'{self.apxs} failed to run') + log.exception(f"{self.apxs} failed to run") return self._httpd_version def versiontuple(self, v): - v = re.sub(r'(\d+\.\d+(\.\d+)?)(-\S+)?', r'\1', v) - return tuple(map(int, v.split('.'))) + v = re.sub(r"(\d+\.\d+(\.\d+)?)(-\S+)?", r"\1", v) + return tuple(map(int, v.split("."))) def httpd_is_at_least(self, minv): if self.httpd_version is None: @@ -344,15 +399,17 @@ def caddy_is_at_least(self, minv): return hv >= self.versiontuple(minv) def is_complete(self) -> bool: - return os.path.isfile(self.httpd) and \ - self.apxs is not None and \ - os.path.isfile(self.apxs) + return ( + os.path.isfile(self.httpd) + and self.apxs is not None + and os.path.isfile(self.apxs) + ) def get_incomplete_reason(self) -> Optional[str]: if self.httpd is None or len(self.httpd.strip()) == 0: - return 'httpd not configured, see `--with-test-httpd=`' + return "httpd not configured, see `--with-test-httpd=`" if not os.path.isfile(self.httpd): - return f'httpd ({self.httpd}) not found' + return f"httpd ({self.httpd}) not found" if self.apxs is None: return "command apxs not found (commonly provided in apache2-dev)" if not os.path.isfile(self.apxs): @@ -372,17 +429,20 @@ def vsftpd_version(self): return self._vsftpd_version @property - def tcpdmp(self) -> Optional[str]: + def h2o_version(self): + return self._h2o_version + + @property + def tcpdump(self) -> Optional[str]: return self._tcpdump def clear_locks(self): - ca_lock = os.path.join(self.gen_root, 'ca/ca.lock') + ca_lock = os.path.join(self.gen_root, "ca/ca.lock") if os.path.exists(ca_lock): os.remove(ca_lock) class Env: - SERVER_TIMEOUT = 30 # seconds to wait for server to come up/reload CONFIG = EnvConfig() @@ -407,98 +467,106 @@ def have_nghttpx() -> bool: def have_h3_server() -> bool: return Env.CONFIG.nghttpx_with_h3 + @staticmethod + def have_h2o() -> bool: + return Env.CONFIG.h2o is not None + @staticmethod def have_ssl_curl() -> bool: - return Env.curl_has_feature('ssl') or Env.curl_has_feature('multissl') + return Env.curl_has_feature("ssl") or Env.curl_has_feature("multissl") @staticmethod def have_h2_curl() -> bool: - return 'http2' in Env.CONFIG.curl_props['features'] + return "http2" in Env.CONFIG.curl_props["features"] @staticmethod def have_h3_curl() -> bool: - return 'http3' in Env.CONFIG.curl_props['features'] + return "http3" in Env.CONFIG.curl_props["features"] @staticmethod def have_compressed_curl() -> bool: - return 'brotli' in Env.CONFIG.curl_props['libs'] or \ - 'zlib' in Env.CONFIG.curl_props['libs'] or \ - 'zstd' in Env.CONFIG.curl_props['libs'] + return ( + "brotli" in Env.CONFIG.curl_props["libs"] + or "zlib" in Env.CONFIG.curl_props["libs"] + or "zstd" in Env.CONFIG.curl_props["libs"] + ) @staticmethod def curl_uses_lib(libname: str) -> bool: - return libname.lower() in Env.CONFIG.curl_props['libs'] + return libname.lower() in Env.CONFIG.curl_props["libs"] @staticmethod def curl_uses_any_libs(libs: List[str]) -> bool: for libname in libs: - if libname.lower() in Env.CONFIG.curl_props['libs']: + if libname.lower() in Env.CONFIG.curl_props["libs"]: return True return False @staticmethod def curl_uses_ossl_quic() -> bool: if Env.have_h3_curl(): - return not Env.curl_uses_lib('ngtcp2') and Env.curl_uses_lib('nghttp3') + return not Env.curl_uses_lib("ngtcp2") and Env.curl_uses_lib("nghttp3") return False @staticmethod def curl_version_string() -> str: - return Env.CONFIG.curl_props['version_string'] + return Env.CONFIG.curl_props["version_string"] @staticmethod def curl_features_string() -> str: - return Env.CONFIG.curl_props['features_string'] + return Env.CONFIG.curl_props["features_string"] @staticmethod def curl_has_feature(feature: str) -> bool: - return feature.lower() in Env.CONFIG.curl_props['features'] + return feature.lower() in Env.CONFIG.curl_props["features"] @staticmethod def curl_protocols_string() -> str: - return Env.CONFIG.curl_props['protocols_string'] + return Env.CONFIG.curl_props["protocols_string"] @staticmethod def curl_has_protocol(protocol: str) -> bool: - return protocol.lower() in Env.CONFIG.curl_props['protocols'] + return protocol.lower() in Env.CONFIG.curl_props["protocols"] @staticmethod def curl_lib_version(libname: str) -> str: - prefix = f'{libname.lower()}/' - for lversion in Env.CONFIG.curl_props['lib_versions']: + prefix = f"{libname.lower()}/" + for lversion in Env.CONFIG.curl_props["lib_versions"]: if lversion.startswith(prefix): - return lversion[len(prefix):] - return 'unknown' + return lversion[len(prefix) :] + return "unknown" @staticmethod def curl_lib_version_at_least(libname: str, min_version) -> bool: lversion = Env.curl_lib_version(libname) - if lversion != 'unknown': - return Env.CONFIG.versiontuple(min_version) <= \ - Env.CONFIG.versiontuple(lversion) + if lversion != "unknown": + return Env.CONFIG.versiontuple(min_version) <= Env.CONFIG.versiontuple( + lversion + ) return False @staticmethod def curl_lib_version_before(libname: str, lib_version) -> bool: lversion = Env.curl_lib_version(libname) - if lversion != 'unknown': - if m := re.match(r'(\d+\.\d+\.\d+).*', lversion): + if lversion != "unknown": + if m := re.match(r"(\d+\.\d+\.\d+).*", lversion): lversion = m.group(1) - return Env.CONFIG.versiontuple(lib_version) > \ - Env.CONFIG.versiontuple(lversion) + return Env.CONFIG.versiontuple(lib_version) > Env.CONFIG.versiontuple( + lversion + ) return False @staticmethod def curl_os() -> str: - return Env.CONFIG.curl_props['os'] + return Env.CONFIG.curl_props["os"] @staticmethod def curl_fullname() -> str: - return Env.CONFIG.curl_props['fullname'] + return Env.CONFIG.curl_props["fullname"] @staticmethod def curl_version() -> str: - return Env.CONFIG.curl_props['version'] + return Env.CONFIG.curl_props["version"] @staticmethod def curl_is_debug() -> bool: @@ -523,37 +591,36 @@ def curl_resolv_threaded() -> bool: @staticmethod def curl_can_early_data() -> bool: if Env.curl_uses_lib('gnutls'): - return Env.curl_lib_version_at_least('gnutls', '3.6.13') + return Env.curl_lib_version_at_least('gnutls', '3.7.2') return Env.curl_uses_any_libs(['wolfssl', 'quictls', 'openssl']) @staticmethod def curl_can_h3_early_data() -> bool: - return Env.curl_can_early_data() and \ - Env.curl_uses_lib('ngtcp2') + return Env.curl_can_early_data() and Env.curl_uses_lib("ngtcp2") @staticmethod def http_protos() -> List[str]: # http protocols we can test if Env.have_h2_curl(): if Env.have_h3(): - return ['http/1.1', 'h2', 'h3'] - return ['http/1.1', 'h2'] - return ['http/1.1'] + return ["http/1.1", "h2", "h3"] + return ["http/1.1", "h2"] + return ["http/1.1"] @staticmethod def http_h1_h2_protos() -> List[str]: # http 1+2 protocols we can test if Env.have_h2_curl(): - return ['http/1.1', 'h2'] - return ['http/1.1'] + return ["http/1.1", "h2"] + return ["http/1.1"] @staticmethod def http_mplx_protos() -> List[str]: # http multiplexing protocols we can test if Env.have_h2_curl(): if Env.have_h3(): - return ['h2', 'h3'] - return ['h2'] + return ["h2", "h3"] + return ["h2"] return [] @staticmethod @@ -572,6 +639,10 @@ def nghttpx_version() -> str: def caddy_version() -> str: return Env.CONFIG.caddy_version + @staticmethod + def h2o_version() -> str: + return Env.CONFIG.h2o_version + @staticmethod def caddy_is_at_least(minv) -> bool: return Env.CONFIG.caddy_is_at_least(minv) @@ -606,26 +677,25 @@ def has_sftpd() -> bool: @staticmethod def tcpdump() -> Optional[str]: - return Env.CONFIG.tcpdmp + return Env.CONFIG.tcpdump def __init__(self, pytestconfig=None, env_config=None): if env_config: Env.CONFIG = env_config - self._verbose = pytestconfig.option.verbose \ - if pytestconfig is not None else 0 + self._verbose = pytestconfig.option.verbose if pytestconfig is not None else 0 self._ca = None self._test_timeout = 300.0 if self._verbose > 1 else 60.0 # seconds def issue_certs(self): if self._ca is None: # ca_dir = os.path.join(self.CONFIG.gen_root, 'ca') - ca_dir = os.path.join(self.gen_dir, 'ca') + ca_dir = os.path.join(self.gen_dir, "ca") os.makedirs(ca_dir, exist_ok=True) - lock_file = os.path.join(ca_dir, 'ca.lock') + lock_file = os.path.join(ca_dir, "ca.lock") with FileLock(lock_file): - self._ca = TestCA.create_root(name=self.CONFIG.tld, - store_dir=ca_dir, - key_type="rsa2048") + self._ca = TestCA.create_root( + name=self.CONFIG.tld, store_dir=ca_dir, key_type="rsa2048" + ) self._ca.issue_certs(self.CONFIG.cert_specs) if self.have_openssl(): self._ca.create_hashdir(self.openssl) @@ -714,19 +784,19 @@ def update_ports(self, ports: Dict[str, int]): @property def http_port(self) -> int: - return self.CONFIG.ports.get('http', 0) + return self.CONFIG.ports.get("http", 0) @property def https_port(self) -> int: - return self.CONFIG.ports['https'] + return self.CONFIG.ports["https"] @property def https_only_tcp_port(self) -> int: - return self.CONFIG.ports['https-tcp-only'] + return self.CONFIG.ports["https-tcp-only"] @property def nghttpx_https_port(self) -> int: - return self.CONFIG.ports['nghttpx_https'] + return self.CONFIG.ports["nghttpx_https"] @property def h3_port(self) -> int: @@ -734,27 +804,36 @@ def h3_port(self) -> int: @property def proxy_port(self) -> int: - return self.CONFIG.ports['proxy'] + return self.CONFIG.ports["proxy"] @property def proxys_port(self) -> int: - return self.CONFIG.ports['proxys'] + return self.CONFIG.ports["proxys"] @property def ftp_port(self) -> int: - return self.CONFIG.ports['ftp'] + return self.CONFIG.ports["ftp"] @property def ftps_port(self) -> int: - return self.CONFIG.ports['ftps'] + return self.CONFIG.ports["ftps"] @property def h2proxys_port(self) -> int: - return self.CONFIG.ports['h2proxys'] + return self.CONFIG.ports["h2proxys"] + + @property + def h3proxys_port(self) -> int: + return self.CONFIG.ports["h2o_h3proxys"] - def pts_port(self, proto: str = 'http/1.1') -> int: + def pts_port(self, proto: str = "http/1.1", use_h2o: bool = False) -> int: # proxy tunnel port - return self.CONFIG.ports['h2proxys' if proto == 'h2' else 'proxys'] + prefix = 'h2o_' if use_h2o else '' + if proto == "h3": + return self.CONFIG.ports.get("h2o_h3proxys", 0) + if proto == "h2": + return self.CONFIG.ports.get(f"{prefix}h2proxys", 0) + return self.CONFIG.ports[f"{prefix}proxys"] @property def caddy(self) -> str: @@ -762,11 +841,11 @@ def caddy(self) -> str: @property def caddy_https_port(self) -> int: - return self.CONFIG.ports['caddys'] + return self.CONFIG.ports["caddys"] @property def caddy_http_port(self) -> int: - return self.CONFIG.ports['caddy'] + return self.CONFIG.ports["caddy"] @property def danted(self) -> str: @@ -778,7 +857,7 @@ def vsftpd(self) -> str: @property def ws_port(self) -> int: - return self.CONFIG.ports['ws'] + return self.CONFIG.ports["ws"] @property def curl(self) -> str: @@ -802,58 +881,66 @@ def nghttpx(self) -> Optional[str]: @property def slow_network(self) -> bool: - return "CURL_DBG_SOCK_WBLOCK" in os.environ or \ - "CURL_DBG_SOCK_WPARTIAL" in os.environ + return ( + "CURL_DBG_SOCK_WBLOCK" in os.environ + or "CURL_DBG_SOCK_WPARTIAL" in os.environ + ) @property def ci_run(self) -> bool: return "CURL_CI" in os.environ def port_for(self, alpn_proto: Optional[str] = None): - if alpn_proto is None or \ - alpn_proto in ['h2', 'http/1.1', 'http/1.0', 'http/0.9']: + if alpn_proto is None or alpn_proto in [ + "h2", + "http/1.1", + "http/1.0", + "http/0.9", + ]: return self.https_port - if alpn_proto in ['h3']: + if alpn_proto in ["h3"]: return self.h3_port return self.http_port def authority_for(self, domain: str, alpn_proto: Optional[str] = None): - return f'{domain}:{self.port_for(alpn_proto=alpn_proto)}' + return f"{domain}:{self.port_for(alpn_proto=alpn_proto)}" - def make_data_file(self, indir: str, fname: str, fsize: int, - line_length: int = 1024) -> str: + def make_data_file( + self, indir: str, fname: str, fsize: int, line_length: int = 1024 + ) -> str: if line_length < 11: - raise RuntimeError('line_length less than 11 not supported') + raise RuntimeError("line_length less than 11 not supported") + os.makedirs(indir, exist_ok=True) fpath = os.path.join(indir, fname) s10 = "0123456789" s = round((line_length / 10) + 1) * s10 - s = s[0:line_length-11] - with open(fpath, 'w') as fd: + s = s[0 : line_length - 11] + with open(fpath, "w") as fd: for i in range(int(fsize / line_length)): fd.write(f"{i:09d}-{s}\n") remain = int(fsize % line_length) if remain != 0: i = int(fsize / line_length) + 1 - fd.write(f"{i:09d}-{s}"[0:remain-1] + "\n") + fd.write(f"{i:09d}-{s}"[0 : remain - 1] + "\n") return fpath def make_data_gzipbomb(self, indir: str, fname: str, fsize: int) -> str: fpath = os.path.join(indir, fname) - gzpath = f'{fpath}.gz' - varpath = f'{fpath}.var' + gzpath = f"{fpath}.gz" + varpath = f"{fpath}.var" - with open(fpath, 'w') as fd: - fd.write('not what we are looking for!\n') + with open(fpath, "w") as fd: + fd.write("not what we are looking for!\n") count = int(fsize / 1024) zero1k = bytearray(1024) - with gzip.open(gzpath, 'wb') as fd: + with gzip.open(gzpath, "wb") as fd: for _ in range(count): fd.write(zero1k) - with open(varpath, 'w') as fd: - fd.write(f'URI: {fname}\n') - fd.write('\n') - fd.write(f'URI: {fname}.gz\n') - fd.write('Content-Type: text/plain\n') - fd.write('Content-Encoding: x-gzip\n') - fd.write('\n') + with open(varpath, "w") as fd: + fd.write(f"URI: {fname}\n") + fd.write("\n") + fd.write(f"URI: {fname}.gz\n") + fd.write("Content-Type: text/plain\n") + fd.write("Content-Encoding: x-gzip\n") + fd.write("\n") return fpath diff --git a/tests/http/testenv/h2o.py b/tests/http/testenv/h2o.py new file mode 100644 index 000000000000..deb3608d3a8c --- /dev/null +++ b/tests/http/testenv/h2o.py @@ -0,0 +1,448 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# *************************************************************************** +# _ _ ____ _ +# 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 +# +########################################################################### +# +import logging +import os +import signal +import socket +import subprocess +import time +from datetime import datetime, timedelta +from typing import Dict, Optional + +from .curl import CurlClient +from .env import Env +from .ports import alloc_ports_and_do + +log = logging.getLogger(__name__) + + +class H2o: + def __init__(self, env: Env, name: str, domain: str, cred_name: str): + self.env = env + self._name = name + self._domain = domain + self._port = 0 # defaults to h3_port + self._cred_name = cred_name + self._loaded_cred_name = None + self._error_fd = None + self._process = None + self._tmp_dir = os.path.join(self.env.gen_dir, self._name) + self._run_dir = os.path.join(self._tmp_dir, "run") + self._conf_file = os.path.join(self._run_dir, "h2o.conf") + self._error_log = os.path.join(self._run_dir, "h2o.log") + self._pid_file = os.path.join(self._run_dir, "h2o.pid") + self._stderr = os.path.join(self._run_dir, "h2o.stderr") + self._cmd = env.CONFIG.h2o + # For proxy subclasses + self._h1_port = None + self._h2_port = None + + @property + def port(self) -> int: + return self._port + + @property + def h1_port(self) -> Optional[int]: + return getattr(self, "_h1_port", None) + + @property + def h2_port(self) -> Optional[int]: + return getattr(self, "_h2_port", None) + + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + + def clear_logs(self): + self._rmf(self._error_log) + self._rmf(self._stderr) + + def dump_logs(self): + lines = [] + lines.append(f"stderr of {self._name}") + lines.append("-------------------------------------------") + self._dump_file(self._stderr, lines) + lines.append("") + lines.append(f"errorlog of {self._name}") + lines.append("-------------------------------------------") + self._dump_file(self._error_log, lines) + lines.append("") + return lines + + def _rmf(self, path): + if os.path.isfile(path): + os.remove(path) + return + + def _dump_file(self, path, lines): + if os.path.isfile(path): + with open(path) as fd: + for line in fd: + lines.append(line.rstrip()) + + def _mkpath(self, path): + if not os.path.exists(path): + os.makedirs(path) + return + + def _log(self, level, msg): + getattr(log, level)(f"[{self._name}] {msg}") + + def is_running(self): + if self._process: + self._process.poll() + return self._process.returncode is None + return False + + def initial_start(self): + self._rmf(self._pid_file) + self._rmf(self._error_log) + self._mkpath(self._run_dir) + self.write_config() + + def start(self, wait_live=True): + self._mkpath(self._tmp_dir) + self._mkpath(self._run_dir) + if self._process: + self.stop() + self._loaded_cred_name = self._cred_name + self.write_config() + args = [self._cmd, "-c", self._conf_file] + self._error_fd = open(self._stderr, "a") + self._process = subprocess.Popen(args=args, stderr=self._error_fd) + if self._process.returncode is not None: + return False + if wait_live: + time.sleep(1) + # fail fast if h2o rejected the config and already exited + self._process.poll() + if self._process.returncode is not None: + self._log("error", + f"h2o exited early (rc={self._process.returncode})" + f" - check {self._stderr} for details") + self._process = None + return False + return not wait_live or self.wait_for_state( + live=True, timeout=timedelta(seconds=Env.SERVER_TIMEOUT) + ) + + def stop(self, wait_dead=True): + self._mkpath(self._tmp_dir) + if self._process: + self._process.terminate() + try: + self._process.wait(timeout=5) + except subprocess.TimeoutExpired: + self._process.kill() + self._process.wait(timeout=2) + self._process = None + self.close_log() + return not wait_dead or self.wait_for_state( + live=False, timeout=timedelta(seconds=5) + ) + self.close_log() + return True + + def kill(self, wait_dead=True): + if self._process: + self._process.kill() + self.close_log() + return True + self.close_log() + return False + + def restart(self): + self.stop() + return self.start() + + def reload(self, timeout: timedelta = timedelta(seconds=Env.SERVER_TIMEOUT)): + if self._process: + running = self._process + self._process = None + os.kill(running.pid, signal.SIGQUIT) + end_wait = datetime.now() + timedelta(seconds=5) + exited = False + if not self.start(wait_live=False): + self._process = running + return False + while datetime.now() < end_wait: + try: + self._log("debug", f"waiting for h2o({running.pid}) to exit.") + running.wait(1) + self._log( + "debug", + f"h2o({running.pid}) terminated -> {running.returncode}", + ) + exited = True + break + except subprocess.TimeoutExpired: + self._log("warning", f"h2o({running.pid}), not shut down yet.") + os.kill(running.pid, signal.SIGQUIT) + if not exited and datetime.now() >= end_wait: + self._log("error", f"h2o({running.pid}), terminate forcefully.") + os.kill(running.pid, signal.SIGKILL) + running.terminate() + running.wait(1) + return self.wait_for_state(live=True, timeout=timeout) + return False + + def wait_for_state( + self, + live: bool, + timeout: timedelta, + url: Optional[str] = None, + log_prefix: str = "h2o", + ): + curl = CurlClient(env=self.env, run_dir=self._tmp_dir) + try_until = datetime.now() + timeout + if url is None: + url = f"https://{self._domain}:{self._port}/" + while datetime.now() < try_until: + if live: + r = curl.http_get( + url=url, extra_args=["--trace", "curl.trace", "--trace-time"] + ) + if r.exit_code == 0: + return True + else: + r = curl.http_get(url=url) + if r.exit_code != 0: + return True + time.sleep(0.1) + if live: + self._log("error", f"Server still not responding after {timeout}") + else: + self._log("debug", f"Server still responding after {timeout}") + return False + + def write_config(self): + # To be overridden by subclasses + with open(self._conf_file, "w") as fd: + fd.write("# h2o test config\n") + + +class H2oServer(H2o): + """h2o HTTP/3 server for testing.""" + + PORT_SPECS = { + "h2o_https": socket.SOCK_STREAM, + } + + def __init__(self, env: Env): + super().__init__( + env=env, name="h2o-server", domain=env.domain1, cred_name=env.domain1 + ) + self._docs_dir = os.path.join(self.env.gen_dir, "docs") + + @property + def docs_dir(self): + return self._docs_dir + + def initial_start(self): + super().initial_start() + + def startup(ports: Dict[str, int]) -> bool: + self._port = ports["h2o_https"] + if self.start(): + self.env.update_ports(ports) + return True + self.stop() + self._port = 0 + return False + + return alloc_ports_and_do( + H2oServer.PORT_SPECS, startup, self.env.gen_root, max_tries=3 + ) + + def write_config(self): + creds = self.env.get_credentials(self._cred_name) + assert creds # convince pytype this is not None + self._mkpath(self._docs_dir) + self._mkpath(self._run_dir) + # Create a simple test file + with open(os.path.join(self._docs_dir, "data.json"), "w") as f: + f.write('{"message": "Hello from h2o HTTP/3 server"}\n') + with open(self._conf_file, "w") as fd: + fd.write(f"""# h2o HTTP/3 server configuration +server-name: "h2o-test-server" +num-threads: 1 + +listen: &ssl_listen + port: {self._port} + ssl: + certificate-file: {creds.cert_file} + key-file: {creds.pkey_file} + neverbleed: OFF + minimum-version: TLSv1.2 + ocsp-update-interval: 0 + +listen: + <<: *ssl_listen + type: quic + +hosts: + "{self._domain}": + paths: + "/": + file.dir: {self._docs_dir} + +http2-reprioritize-blocking-assets: ON + +access-log: {self._run_dir}/access.log +error-log: {self._error_log} +""") + + +class H2oProxy(H2o): + """h2o MASQUE proxy for testing.""" + + def __init__(self, env: Env): + super().__init__( + env=env, + name="h2o-proxy", + domain=env.proxy_domain, + cred_name=env.proxy_domain, + ) + + def initial_start(self): + super().initial_start() + + def startup(ports: Dict[str, int]) -> bool: + self._port = ports["h2o_h3proxys"] + self._h2_port = ports["h2o_h2proxys"] + self._h1_port = ports["h2o_proxys"] + if self.start(): + self.env.update_ports(ports) + return True + self.stop() + self._port = 0 + self._h2_port = 0 + self._h1_port = 0 + return False + + return alloc_ports_and_do( + { + "h2o_h3proxys": socket.SOCK_DGRAM, + "h2o_h2proxys": socket.SOCK_STREAM, + "h2o_proxys": socket.SOCK_STREAM, + }, + startup, + self.env.gen_root, + max_tries=3, + ) + + def write_config(self): + creds = self.env.get_credentials(self._cred_name) + assert creds # convince pytype this is not None + self._mkpath(self._run_dir) + with open(self._conf_file, "w") as fd: + fd.write(f"""# h2o MASQUE proxy configuration +server-name: "h2o-test-proxy" +num-threads: 1 + +proxy.tunnel: ON + +# HTTP/1.1 proxy listener +listen: &h1_listen + port: {getattr(self, "_h1_port", self._port)} + ssl: + certificate-file: {creds.cert_file} + key-file: {creds.pkey_file} + neverbleed: OFF + minimum-version: TLSv1.2 + ocsp-update-interval: 0 + +# HTTP/2 proxy listener +listen: &h2_listen + port: {getattr(self, "_h2_port", self._port)} + ssl: + certificate-file: {creds.cert_file} + key-file: {creds.pkey_file} + neverbleed: OFF + minimum-version: TLSv1.2 + ocsp-update-interval: 0 + +# HTTP/3 proxy listener (main port) +listen: &h3_listen + port: {self._port} + ssl: + certificate-file: {creds.cert_file} + key-file: {creds.pkey_file} + neverbleed: OFF + minimum-version: TLSv1.2 + ocsp-update-interval: 0 + +# QUIC listener for HTTP/3 +listen: + <<: *h3_listen + type: quic + +hosts: + "{self._domain}": + paths: + "/": + proxy.connect: [+*] + proxy.ssl.verify-peer: OFF + "/.well-known/masque/udp": + proxy.connect-udp: [+*] + proxy.ssl.verify-peer: OFF + +http2-reprioritize-blocking-assets: ON + +access-log: {self._run_dir}/access.log +error-log: {self._error_log} +""") + + def wait_for_state( + self, + live: bool, + timeout: timedelta, + url: Optional[str] = None, + log_prefix: str = "h2o", + ): + curl = CurlClient(env=self.env, run_dir=self._tmp_dir) + try_until = datetime.now() + timeout + if url is None: + url = f"https://{self.env.proxy_domain}:{self._port}/" + while datetime.now() < try_until: + if live: + r = curl.http_get( + url=url, extra_args=["--trace", "curl.trace", "--trace-time"] + ) + if r.exit_code == 0: + return True + else: + r = curl.http_get(url=url) + if r.exit_code != 0: + return True + time.sleep(0.1) + if live: + self._log("error", f"Proxy still not responding after {timeout}") + else: + self._log("debug", f"Proxy still responding after {timeout}") + return False diff --git a/tests/http/testenv/mod_curltest/mod_curltest.c b/tests/http/testenv/mod_curltest/mod_curltest.c index 5e0f6400fb57..bd09c8b377d8 100644 --- a/tests/http/testenv/mod_curltest/mod_curltest.c +++ b/tests/http/testenv/mod_curltest/mod_curltest.c @@ -339,7 +339,7 @@ static int curltest_tweak_handler(request_rec *r) } } else if(!strcmp("id", arg)) { - /* just an id for repeated requests with curl's URL globbing */ + /* an id for repeated requests with curl's URL globbing */ request_id = val; continue; } @@ -551,7 +551,7 @@ static int curltest_put_handler(request_rec *r) *s = '\0'; val = s + 1; if(!strcmp("id", arg)) { - /* just an id for repeated requests with curl's URL globbing */ + /* an id for repeated requests with curl's URL globbing */ request_id = val; continue; } @@ -748,7 +748,7 @@ static int curltest_sslinfo_handler(request_rec *r) *s = '\0'; val = s + 1; if(!strcmp("id", arg)) { - /* just an id for repeated requests with curl's URL globbing */ + /* an id for repeated requests with curl's URL globbing */ request_id = val; continue; } @@ -862,7 +862,7 @@ static int curltest_limit_handler(request_rec *r) *s = '\0'; val = s + 1; if(!strcmp("id", arg)) { - /* just an id for repeated requests with curl's URL globbing */ + /* an id for repeated requests with curl's URL globbing */ request_id = val; continue; } diff --git a/tests/http/testenv/nghttpx.py b/tests/http/testenv/nghttpx.py index 0d95a34bceb9..37c72ad9451f 100644 --- a/tests/http/testenv/nghttpx.py +++ b/tests/http/testenv/nghttpx.py @@ -47,7 +47,7 @@ def __init__(self, env: Env, name: str, domain: str, cred_name: str): self._name = name self._domain = domain self._port = 0 - self._https_port = 0 + self._port_is_quic = False self._cmd = env.nghttpx self._run_dir = os.path.join(env.gen_dir, name) self._pid_file = os.path.join(self._run_dir, 'nghttpx.pid') @@ -55,6 +55,7 @@ def __init__(self, env: Env, name: str, domain: str, cred_name: str): self._error_log = os.path.join(self._run_dir, 'nghttpx.log') self._stderr = os.path.join(self._run_dir, 'nghttpx.stderr') self._tmp_dir = os.path.join(self._run_dir, 'tmp') + self._error_fd = None self._process: Optional[subprocess.Popen] = None self._cred_name = self._def_cred_name = cred_name self._loaded_cred_name = '' @@ -76,12 +77,21 @@ def reload_if_config_changed(self): return self.reload() @property - def https_port(self): - return self._https_port + def port(self): + return self._port + + @property + def port_is_quic(self): + return self._port_is_quic def exists(self): return self._cmd and os.path.exists(self._cmd) + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) self._rmf(self._stderr) @@ -112,7 +122,9 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None + self.close_log() return not wait_dead or self.wait_dead(timeout=timedelta(seconds=5)) + self.close_log() return True def restart(self): @@ -150,18 +162,14 @@ def wait_dead(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) try_until = datetime.now() + timeout while datetime.now() < try_until: - if self._https_port > 0: - check_url = f'https://{self._domain}:{self._port}/' - r = curl.http_get(url=check_url, extra_args=[ - '--trace', 'curl.trace', '--trace-time', - '--connect-timeout', '1' - ]) - else: - check_url = f'https://{self._domain}:{self._port}/' - r = curl.http_get(url=check_url, extra_args=[ - '--trace', 'curl.trace', '--trace-time', - '--http3-only', '--connect-timeout', '1' - ]) + xargs = [ + '--trace', 'curl.trace', '--trace-time', + '--connect-timeout', '1' + ] + if self.port_is_quic: + xargs.extend(['--http3-only']) + check_url = f'https://{self._domain}:{self.port}/' + r = curl.http_get(url=check_url, extra_args=xargs) if r.exit_code != 0: return True log.debug(f'waiting for nghttpx to stop responding: {r}') @@ -173,18 +181,14 @@ def wait_live(self, timeout: timedelta): curl = CurlClient(env=self.env, run_dir=self._tmp_dir) try_until = datetime.now() + timeout while datetime.now() < try_until: - if self._https_port > 0: - check_url = f'https://{self._domain}:{self._port}/' - r = curl.http_get(url=check_url, extra_args=[ - '--trace', 'curl.trace', '--trace-time', - '--connect-timeout', '1' - ]) - else: - check_url = f'https://{self._domain}:{self._port}/' - r = curl.http_get(url=check_url, extra_args=[ - '--http3-only', '--trace', 'curl.trace', '--trace-time', - '--connect-timeout', '1' - ]) + xargs = [ + '--trace', 'curl.trace', '--trace-time', + '--connect-timeout', '1' + ] + if self.port_is_quic: + xargs.extend(['--http3-only']) + check_url = f'https://{self._domain}:{self.port}/' + r = curl.http_get(url=check_url, extra_args=xargs) if r.exit_code == 0: return True time.sleep(.1) @@ -216,13 +220,18 @@ class NghttpxQuic(Nghttpx): def __init__(self, env: Env): super().__init__(env=env, name='nghttpx-quic', domain=env.domain1, cred_name=env.domain1) - self._https_port = env.https_port + self._https_port = 0 def initial_start(self): super().initial_start() def startup(ports: Dict[str, int]) -> bool: - self._port = ports['nghttpx_https'] + self._https_port = ports['nghttpx_https'] + if self.supports_h3(): + self._port = self.env.h3_port + self._port_is_quic = True + else: + self._port = self._https_port if self.start(): self.env.update_ports(ports) return True @@ -240,10 +249,10 @@ def start(self, wait_live=True): creds = self.env.get_credentials(self._cred_name) assert creds # convince pytype this is not None self._loaded_cred_name = self._cred_name - args = [self._cmd, f'--frontend=*,{self._port};tls'] + args = [self._cmd, f'--frontend=*,{self._https_port};tls'] if self.supports_h3(): args.extend([ - f'--frontend=*,{self.env.h3_port};quic', + f'--frontend=*,{self._port};quic', '--frontend-quic-early-data', ]) args.extend([ @@ -261,8 +270,8 @@ def start(self, wait_live=True): '--frontend-http3-max-connection-window-size=100M', # f'--frontend-quic-debug-log', ]) - ngerr = open(self._stderr, 'a') - self._process = subprocess.Popen(args=args, stderr=ngerr) + self._error_fd = open(self._stderr, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) @@ -311,8 +320,8 @@ def start(self, wait_live=True): creds.pkey_file, creds.cert_file, ] - ngerr = open(self._stderr, 'a') - self._process = subprocess.Popen(args=args, stderr=ngerr) + self._error_fd = open(self._stderr, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/sshd.py b/tests/http/testenv/sshd.py index e800ced17b67..de8078dd265c 100644 --- a/tests/http/testenv/sshd.py +++ b/tests/http/testenv/sshd.py @@ -74,6 +74,7 @@ def __init__(self, env: Env): ] self._user_key_files = [] self._user_pub_files = [] + self._error_fd = None self._process = None self.clear_logs() @@ -132,7 +133,8 @@ def mk_host_keys(self): self._host_key_files.append(key_file) pub_file = f'{key_file}.pub' self._host_pub_files.append(pub_file) - pubkey = open(pub_file).read() + with open(pub_file) as fp: + pubkey = fp.read() # fd_known.write(f'[127.0.0.1]:{self.port} {pubkey}') fd_known.write(f'[{self.env.domain1.lower()}]:{self.port} {pubkey}') fd_unknown.write(f'dummy.invalid {pubkey}') @@ -159,17 +161,25 @@ def mk_user_keys(self): self._user_pub_files.append(f'{key_file}.pub') with open(self._auth_keys, 'w') as fd: os.chmod(self._auth_keys, stat.S_IRUSR | stat.S_IWUSR) - pubkey = open(self._user_pub_files[0]).read() + with open(self._user_pub_files[0]) as fp: + pubkey = fp.read() fd.write(pubkey) + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._sshd_log) def dump_log(self): lines = ['>>--sshd log ----------------------------------------------\n'] - lines.extend(open(self._sshd_log)) + with open(self._sshd_log) as fd: + lines.extend(fd.readlines()) lines.extend(['>>--curl log ----------------------------------------------\n']) - lines.extend(open(os.path.join(self._tmp_dir, 'curl.stderr'))) + with open(os.path.join(self._tmp_dir, 'curl.stderr')) as fd: + lines.extend(fd.readlines()) lines.append('<<-------------------------------------------------------\n') return ''.join(lines) @@ -193,7 +203,7 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None - return not wait_dead or True + self.close_log() return True def restart(self): @@ -231,8 +241,8 @@ def start(self, wait_live=True): run_env = os.environ.copy() # does not have any effect, sadly # run_env['HOME'] = f'{self._home_dir}' - procerr = open(self._sshd_log, 'a') - self._process = subprocess.Popen(args=args, stderr=procerr, env=run_env) + self._error_fd = open(self._sshd_log, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd, env=run_env) if self._process.returncode is not None: return False return self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/vsftpd.py b/tests/http/testenv/vsftpd.py index ace2884c7d35..6c2d1db8c5f9 100644 --- a/tests/http/testenv/vsftpd.py +++ b/tests/http/testenv/vsftpd.py @@ -68,6 +68,7 @@ def __init__(self, env: Env, with_ssl=False, ssl_implicit=False): self._conf_file = os.path.join(self._vsftpd_dir, 'test.conf') self._pid_file = os.path.join(self._vsftpd_dir, 'vsftpd.pid') self._error_log = os.path.join(self._vsftpd_dir, 'vsftpd.log') + self._error_fd = None self._process = None self.clear_logs() @@ -84,6 +85,11 @@ def docs_dir(self): def port(self) -> int: return self._port + def close_log(self): + if self._error_fd: + self._error_fd.close() + self._error_fd = None + def clear_logs(self): self._rmf(self._error_log) @@ -107,7 +113,9 @@ def stop(self, wait_dead=True): self._process.terminate() self._process.wait(timeout=2) self._process = None + self.close_log() return not wait_dead or self.wait_dead(timeout=timedelta(seconds=5)) + self.close_log() return True def restart(self): @@ -138,8 +146,8 @@ def start(self, wait_live=True): self._cmd, f'{self._conf_file}', ] - procerr = open(self._error_log, 'a') - self._process = subprocess.Popen(args=args, stderr=procerr) + self._error_fd = open(self._error_log, 'a') + self._process = subprocess.Popen(args=args, stderr=self._error_fd) if self._process.returncode is not None: return False return not wait_live or self.wait_live(timeout=timedelta(seconds=Env.SERVER_TIMEOUT)) diff --git a/tests/http/testenv/ws_echo_server.py b/tests/http/testenv/ws_echo_server.py index 99eaa628d239..f3f659a27f78 100755 --- a/tests/http/testenv/ws_echo_server.py +++ b/tests/http/testenv/ws_echo_server.py @@ -37,6 +37,7 @@ async def echo(websocket): async for message in websocket: await websocket.send(message) except ConnectionClosedError: + # websocket connection closed by client pass diff --git a/tests/http2-server.pl b/tests/http2-server.pl index 3ab38803f174..0729a00593a5 100755 --- a/tests/http2-server.pl +++ b/tests/http2-server.pl @@ -110,7 +110,7 @@ my $certfile = abs_path("certs/$cert.pem"); my $keyfile = abs_path("certs/$cert.key"); -my $cmdline="$nghttpx --backend=$connect ". +my $cmdline = "$nghttpx --backend=$connect ". "--backend-keep-alive-timeout=500ms ". "--frontend=\"*,$listenport;no-tls\" ". "--frontend=\"*,$listenport2\" ". diff --git a/tests/http3-server.pl b/tests/http3-server.pl index 13cc49a9dff0..b86a6a58b388 100755 --- a/tests/http3-server.pl +++ b/tests/http3-server.pl @@ -110,7 +110,7 @@ my $certfile = abs_path("certs/$cert.pem"); my $keyfile = abs_path("certs/$cert.key"); -my $cmdline="$nghttpx --http2-proxy --backend=$connect ". +my $cmdline = "$nghttpx --http2-proxy --backend=$connect ". "--backend-keep-alive-timeout=500ms ". "--frontend=\"*,$listenport\" ". "--frontend=\"*,$listenport;quic\" ". diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 724636464aab..bec648542b4f 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -96,22 +96,26 @@ TESTS_C = \ lib1552.c lib1553.c lib1554.c lib1555.c lib1556.c lib1557.c lib1558.c \ lib1559.c lib1560.c lib1564.c lib1565.c \ lib1567.c lib1568.c lib1569.c lib1571.c \ - lib1576.c lib1582.c lib1587.c lib1588.c \ + lib1576.c lib1582.c lib1587.c lib1588.c lib1589.c \ lib1591.c lib1592.c lib1593.c lib1594.c lib1597.c \ lib1598.c lib1599.c \ + lib1647.c lib1648.c lib1649.c \ lib1662.c \ + lib1686.c \ lib1900.c lib1901.c lib1902.c lib1903.c lib1905.c lib1906.c lib1907.c \ lib1908.c lib1910.c lib1911.c lib1912.c lib1913.c \ - lib1915.c lib1916.c lib1918.c lib1919.c lib1920.c \ + lib1915.c lib1916.c lib1918.c lib1919.c lib1920.c lib1921.c \ + lib1922.c \ lib1933.c lib1934.c lib1935.c lib1936.c lib1937.c lib1938.c lib1939.c \ lib1940.c lib1945.c \ lib1947.c lib1948.c \ lib1955.c lib1956.c lib1957.c lib1958.c lib1959.c lib1960.c \ - lib1964.c lib1965.c lib1970.c \ + lib1964.c lib1965.c lib1967.c lib1970.c \ lib1971.c lib1972.c lib1973.c lib1974.c lib1975.c lib1977.c lib1978.c \ lib2023.c lib2032.c lib2082.c \ lib2301.c lib2302.c lib2304.c lib2306.c lib2308.c lib2309.c \ lib2402.c lib2404.c lib2405.c \ + lib2412.c \ lib2502.c lib2504.c lib2505.c lib2506.c \ lib2700.c \ lib3010.c lib3025.c lib3026.c lib3027.c lib3033.c lib3034.c \ diff --git a/tests/libtest/cli_ftp_upload.c b/tests/libtest/cli_ftp_upload.c index 32835bc12329..7e05807b5c73 100644 --- a/tests/libtest/cli_ftp_upload.c +++ b/tests/libtest/cli_ftp_upload.c @@ -71,7 +71,7 @@ static CURLcode test_cli_ftp_upload(const char *URL) CURL *curl_handle; int running_handles = 0; int max_fd = -1; - struct timeval timeout = { 1, 0 }; + struct timeval timeout; fd_set fdread; fd_set fdwrite; fd_set fdexcep; @@ -82,6 +82,9 @@ static CURLcode test_cli_ftp_upload(const char *URL) CURLcode result = CURLE_FAILED_INIT; curl_off_t uploadsize = -1; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + (void)URL; while((ch = cgetopt(test_argc, test_argv, "r:")) != -1) { switch(ch) { @@ -169,7 +172,7 @@ static CURLcode test_cli_ftp_upload(const char *URL) curl_global_cleanup(); curl_slist_free_all(host); - curl_mfprintf(stderr, "transfer result: %d\n", result); + curl_mfprintf(stderr, "transfer result: %d\n", (int)result); return result; #else /* !CURL_DISABLE_FTP */ (void)URL; diff --git a/tests/libtest/cli_h2_pausing.c b/tests/libtest/cli_h2_pausing.c index ac57b5c5f8aa..bf98d03ecae2 100644 --- a/tests/libtest/cli_h2_pausing.c +++ b/tests/libtest/cli_h2_pausing.c @@ -265,7 +265,7 @@ static CURLcode test_cli_h2_pausing(const char *URL) curl_mfprintf(stderr, "ERROR: [%zu] done, paused=%d, " "resumed=%d, result %d - wtf?\n", i, handles[i].paused, - handles[i].resumed, msg->data.result); + handles[i].resumed, (int)msg->data.result); result = (CURLcode)1; goto cleanup; } diff --git a/tests/libtest/cli_h2_upgrade_extreme.c b/tests/libtest/cli_h2_upgrade_extreme.c index 62aa0bc36145..9df8640cffec 100644 --- a/tests/libtest/cli_h2_upgrade_extreme.c +++ b/tests/libtest/cli_h2_upgrade_extreme.c @@ -126,7 +126,7 @@ static CURLcode test_cli_h2_upgrade_extreme(const char *URL) } else if(msg->data.result) { curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T - ": failed with %d\n", xfer_id, msg->data.result); + ": failed with %d\n", xfer_id, (int)msg->data.result); goto cleanup; } else if(status != 206) { diff --git a/tests/libtest/cli_hx_download.c b/tests/libtest/cli_hx_download.c index fca5a7dec110..b472ed5691b4 100644 --- a/tests/libtest/cli_hx_download.c +++ b/tests/libtest/cli_hx_download.c @@ -147,7 +147,7 @@ static int my_progress_d_cb(void *userdata, result = curl_easy_getinfo(t->curl, CURLINFO_TLS_SSL_PTR, &tls); if(result) { curl_mfprintf(stderr, "[t-%zu] info CURLINFO_TLS_SSL_PTR failed: %d\n", - t->idx, result); + t->idx, (int)result); assert(0); } else { @@ -199,7 +199,7 @@ static int my_progress_d_cb(void *userdata, (struct rustls_connection *)tls->internals); assert(v); curl_mfprintf(stderr, "[t-%zu] info rustls TLS version 0x%x\n", - t->idx, v); + t->idx, (unsigned int)v); break; } #endif @@ -214,13 +214,13 @@ static int my_progress_d_cb(void *userdata, assert(sspi_status == SEC_E_OK); (void)sspi_status; curl_mfprintf(stderr, "[t-%zu] info Schannel TLS version 0x%08lx\n", - t->idx, info.dwProtocol); + t->idx, (unsigned long)info.dwProtocol); break; } #endif default: curl_mfprintf(stderr, "[t-%zu] info SSL_PTR backend=%d, ptr=%p\n", - t->idx, tls->backend, tls->internals); + t->idx, (int)tls->backend, tls->internals); break; } } @@ -287,7 +287,7 @@ static void usage_hx_download(const char *msg) " -r :: resolve information\n" " -T number max concurrent connections total\n" " -V http_version (http/1.1, h2, h3) http version to use\n" - " -6 use ipv6 for resolving the FIRST url\n" + " -6 use ipv6 for resolving the FIRST URL\n" ); } @@ -502,7 +502,7 @@ static CURLcode test_cli_hx_download(const char *URL) t->done = 1; t->result = m->data.result; curl_mfprintf(stderr, "[t-%zu] FINISHED with result %d\n", - t->idx, t->result); + t->idx, (int)t->result); if(use_earlydata) { curl_off_t sent; curl_easy_getinfo(easy, CURLINFO_EARLYDATA_SENT_T, &sent); @@ -587,7 +587,7 @@ static CURLcode test_cli_hx_download(const char *URL) } if(t->result) result = t->result; - else /* on success we expect ssl to have been checked */ + else /* on success we expect SSL to have been checked */ assert(t->checked_ssl); } curlx_free(transfer_d); diff --git a/tests/libtest/cli_hx_upload.c b/tests/libtest/cli_hx_upload.c index d0434a2eab1b..64c3bbb3a508 100644 --- a/tests/libtest/cli_hx_upload.c +++ b/tests/libtest/cli_hx_upload.c @@ -395,7 +395,7 @@ static CURLcode test_cli_hx_upload(const char *URL) curl_mfprintf(stderr, "[t-%zu] STARTING\n", t->idx); rc = curl_easy_perform(curl); - curl_mfprintf(stderr, "[t-%zu] DONE -> %d\n", t->idx, rc); + curl_mfprintf(stderr, "[t-%zu] DONE -> %d\n", t->idx, (int)rc); t->curl = NULL; curl_easy_reset(curl); } @@ -448,7 +448,7 @@ static CURLcode test_cli_hx_upload(const char *URL) t->done = 1; curl_mfprintf(stderr, "[t-%zu] FINISHED, " "result=%d, response=%ld\n", - t->idx, m->data.result, res_status); + t->idx, (int)m->data.result, res_status); if(use_earlydata) { curl_off_t sent; curl_easy_getinfo(easy, CURLINFO_EARLYDATA_SENT_T, &sent); diff --git a/tests/libtest/cli_tls_session_reuse.c b/tests/libtest/cli_tls_session_reuse.c index 031d4cfc2ac6..ba97485a96c2 100644 --- a/tests/libtest/cli_tls_session_reuse.c +++ b/tests/libtest/cli_tls_session_reuse.c @@ -209,7 +209,7 @@ static CURLcode test_cli_tls_session_reuse(const char *URL) } else if(msg->data.result) { curl_mfprintf(stderr, "transfer #%" CURL_FORMAT_CURL_OFF_T - ": failed with %d\n", xfer_id, msg->data.result); + ": failed with %d\n", xfer_id, (int)msg->data.result); goto cleanup; } else if(status != 200) { diff --git a/tests/libtest/cli_upload_pausing.c b/tests/libtest/cli_upload_pausing.c index c52117bf7887..228304368a32 100644 --- a/tests/libtest/cli_upload_pausing.c +++ b/tests/libtest/cli_upload_pausing.c @@ -169,12 +169,12 @@ static CURLcode test_cli_upload_pausing(const char *URL) /* We want to use our own read function. */ curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); - /* It will help us to continue the read function. */ + /* It helps us to continue the read function. */ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, curl); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - /* It will help us to ensure that keepalive does not help. */ + /* It helps us to ensure that keepalive does not help. */ curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 1L); curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 1L); diff --git a/tests/libtest/cli_ws_data.c b/tests/libtest/cli_ws_data.c index 856d41ba1f26..20d15c89d8ae 100644 --- a/tests/libtest/cli_ws_data.c +++ b/tests/libtest/cli_ws_data.c @@ -37,13 +37,13 @@ static CURLcode test_ws_data_m2_check_recv(const struct curl_ws_frame *frame, if(frame->flags & CURLWS_CLOSE) { curl_mfprintf(stderr, "recv_data: unexpected CLOSE frame from server, " "got %zu bytes, offset=%zu, rflags %x\n", - nread, r_offset, frame->flags); + nread, r_offset, (unsigned int)frame->flags); return CURLE_RECV_ERROR; } if(!r_offset && !(frame->flags & CURLWS_BINARY)) { curl_mfprintf(stderr, "recv_data: wrong frame, got %zu bytes, offset=%zu, " "rflags %x\n", - nread, r_offset, frame->flags); + nread, r_offset, (unsigned int)frame->flags); return CURLE_RECV_ERROR; } if(frame->offset != (curl_off_t)r_offset) { @@ -104,7 +104,7 @@ static CURLcode test_ws_data_m2_echo(const char *url, curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); if(result != CURLE_OK) goto out; @@ -120,8 +120,8 @@ static CURLcode test_ws_data_m2_echo(const char *url, sblock = (result == CURLE_AGAIN); if(!result || (result == CURLE_AGAIN)) { curl_mfprintf(stderr, "curl_ws_send(len=%zu) -> %d, " - "%zu (%" CURL_FORMAT_CURL_OFF_T "/%zu)\n", - slen, result, nwritten, (curl_off_t)(len - slen), len); + "%zu (%" CURL_FORMAT_CURL_OFF_T "/%zu)\n", slen, + (int)result, nwritten, (curl_off_t)(len - slen), len); sbuf += nwritten; slen -= nwritten; } @@ -140,8 +140,8 @@ static CURLcode test_ws_data_m2_echo(const char *url, &nread, &frame); if(!result || (result == CURLE_AGAIN)) { rblock = (result == CURLE_AGAIN); - curl_mfprintf(stderr, "curl_ws_recv(len=%zu) -> %d, %zu (%ld/%zu) " - "\n", rlen, result, nread, (long)(len - rlen), len); + curl_mfprintf(stderr, "curl_ws_recv(len=%zu) -> %d, %zu (%ld/%zu)\n", + rlen, (int)result, nread, (long)(len - rlen), len); if(!result) { result = test_ws_data_m2_check_recv(frame, len - rlen, nread, len); if(result) diff --git a/tests/libtest/cli_ws_pingpong.c b/tests/libtest/cli_ws_pingpong.c index 2ed12c8cab5a..32961da72123 100644 --- a/tests/libtest/cli_ws_pingpong.c +++ b/tests/libtest/cli_ws_pingpong.c @@ -78,7 +78,7 @@ static CURLcode test_cli_ws_pingpong(const char *URL) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); if(result == CURLE_OK) result = pingpong(curl, payload); diff --git a/tests/libtest/first.c b/tests/libtest/first.c index e4e9dbd8c8e4..a512cddae975 100644 --- a/tests/libtest/first.c +++ b/tests/libtest/first.c @@ -55,6 +55,7 @@ int select_wrapper(int nfds, fd_set *rd, fd_set *wr, fd_set *exc, const char *libtest_arg2 = NULL; const char *libtest_arg3 = NULL; const char *libtest_arg4 = NULL; +const char *libtest_arg5 = NULL; int test_argc; const char **test_argv; int testnum; @@ -78,7 +79,7 @@ int cgetopt(int argc, const char * const argv[], const char *optstring) } arg = argv[coptind]; - if(arg && strcmp(arg, "--") == 0) { + if(arg && !strcmp(arg, "--")) { coptind++; return -1; } @@ -168,7 +169,7 @@ CURLcode ws_send_ping(CURL *curl, const char *send_payload) CURLcode result = curl_ws_send(curl, send_payload, strlen(send_payload), &sent, 0, CURLWS_PING); curl_mfprintf(stderr, "ws: curl_ws_send returned %d, sent %zu\n", - result, sent); + (int)result, sent); return result; } @@ -180,13 +181,13 @@ CURLcode ws_recv_pong(CURL *curl, const char *expected_payload) CURLcode result = curl_ws_recv(curl, buffer, sizeof(buffer), &rlen, &meta); if(result) { curl_mfprintf(stderr, "ws: curl_ws_recv returned %d, received %zu\n", - result, rlen); + (int)result, rlen); return result; } if(!(meta->flags & CURLWS_PONG)) { curl_mfprintf(stderr, "recv_pong: wrong frame, got %zu bytes rflags %x\n", - rlen, meta->flags); + rlen, (unsigned int)meta->flags); return CURLE_RECV_ERROR; } @@ -200,13 +201,13 @@ CURLcode ws_recv_pong(CURL *curl, const char *expected_payload) return CURLE_RECV_ERROR; } -/* just close the connection */ +/* close the connection */ void ws_close(CURL *curl) { size_t sent; CURLcode result = curl_ws_send(curl, "", 0, &sent, 0, CURLWS_CLOSE); curl_mfprintf(stderr, "ws: curl_ws_send returned %d, sent %zu\n", - result, sent); + (int)result, sent); } #endif /* CURL_DISABLE_WEBSOCKETS */ @@ -247,7 +248,7 @@ int main(int argc, const char **argv) entry_name = argv[1]; entry_func = NULL; for(tmp = 0; s_entries[tmp].ptr; ++tmp) { - if(strcmp(entry_name, s_entries[tmp].name) == 0) { + if(!strcmp(entry_name, s_entries[tmp].name)) { entry_func = s_entries[tmp].ptr; break; } @@ -272,6 +273,9 @@ int main(int argc, const char **argv) if(argc > 5) libtest_arg4 = argv[5]; + if(argc > 6) + libtest_arg5 = argv[6]; + testnum = 0; env = getenv("CURL_TESTNUM"); if(env) { @@ -285,7 +289,7 @@ int main(int argc, const char **argv) #endif result = entry_func(URL); - curl_mfprintf(stderr, "Test ended with result %d\n", result); + curl_mfprintf(stderr, "Test ended with result %d\n", (int)result); #ifdef _WIN32 /* flush buffers of all streams regardless of mode */ @@ -294,5 +298,5 @@ int main(int argc, const char **argv) /* Regular program status codes are limited to 0..127 and 126 and 127 have * special meanings by the shell, so limit a normal return code to 125 */ - return (int)result <= 125 ? (int)result : 125; + return result <= 125 ? result : 125; } diff --git a/tests/libtest/first.h b/tests/libtest/first.h index 062cd169be09..fda2fd9f1ff4 100644 --- a/tests/libtest/first.h +++ b/tests/libtest/first.h @@ -31,6 +31,7 @@ we need both of them in the include path), so that we get good in-depth knowledge about the system we are building this on */ #include "curl_setup.h" +#include "testutil.h" typedef CURLcode (*entry_func_t)(const char *); @@ -54,27 +55,14 @@ extern int unitfail; /* for unittests */ #include "curlx/wait.h" /* for curlx_wait_ms() */ #ifdef HAVE_SYS_SELECT_H -/* since so many tests use select(), we can just as well include it here */ +/* since so many tests use select(), we can as well include it here */ #include #endif -#define test_setopt(A, B, C) \ - do { \ - result = curl_easy_setopt(A, B, C); \ - if(result != CURLE_OK) \ - goto test_cleanup; \ - } while(0) - -#define test_multi_setopt(A, B, C) \ - do { \ - result = curl_multi_setopt(A, B, C); \ - if(result != CURLE_OK) \ - goto test_cleanup; \ - } while(0) - extern const char *libtest_arg2; /* set by first.c to the argv[2] or NULL */ extern const char *libtest_arg3; /* set by first.c to the argv[3] or NULL */ extern const char *libtest_arg4; /* set by first.c to the argv[4] or NULL */ +extern const char *libtest_arg5; /* set by first.c to the argv[5] or NULL */ /* argc and argv as passed in to the main() function */ extern int test_argc; @@ -94,16 +82,15 @@ extern char *hexdump(const unsigned char *buf, size_t len); #ifndef CURL_DISABLE_WEBSOCKETS CURLcode ws_send_ping(CURL *curl, const char *send_payload); CURLcode ws_recv_pong(CURL *curl, const char *expected_payload); -void ws_close(CURL *curl); /* just close the connection */ +void ws_close(CURL *curl); /* close the connection */ #endif /* - * TEST_ERR_* values must within the CURLcode range to not cause compiler + * TEST_ERR_* values must be within the CURLcode range to not cause compiler * errors. * * For portability reasons TEST_ERR_* values should be less than 127. */ - #define TEST_ERR_MAJOR_BAD CURLE_OBSOLETE20 #define TEST_ERR_RUNS_FOREVER CURLE_OBSOLETE24 #define TEST_ERR_EASY_INIT CURLE_OBSOLETE29 @@ -141,10 +128,11 @@ void ws_close(CURL *curl); /* just close the connection */ * should be immediately followed by checking if 'res' variable has been * set. * - * 'res' variable when set will hold a CURLcode, CURLMcode, or any of the + * 'res' variable when set holds a CURLcode, CURLMcode, or any of the * TEST_ERR_* values defined above. It is advisable to return this value * as test result. */ +#ifndef UNITTESTS /* ---------------------------------------------------------------- */ @@ -196,16 +184,14 @@ void ws_close(CURL *curl); /* just close the connection */ /* ---------------------------------------------------------------- */ -#define exe_easy_setopt(A, B, C, Y, Z) \ - do { \ - CURLcode ec = curl_easy_setopt(A, B, C); \ - if(ec != CURLE_OK) { \ - curl_mfprintf(stderr, \ - "%s:%d curl_easy_setopt() failed, " \ - "with code %d (%s)\n", \ - Y, Z, ec, curl_easy_strerror(ec)); \ - result = ec; \ - } \ +#define exe_easy_setopt(A, B, C, Y, Z) \ + do { \ + result = curl_easy_setopt(A, B, C); \ + if(result) \ + curl_mfprintf(stderr, \ + "%s:%d curl_easy_setopt() failed, " \ + "with code %d (%s)\n", \ + Y, Z, (int)result, curl_easy_strerror(result)); \ } while(0) #define res_easy_setopt(A, B, C) \ @@ -235,9 +221,6 @@ void ws_close(CURL *curl); /* just close the connection */ } \ } while(0) -#define res_multi_setopt(A, B, C) \ - exe_multi_setopt(A, B, C, __FILE__, __LINE__) - #define chk_multi_setopt(A, B, C, Y, Z) \ do { \ exe_multi_setopt(A, B, C, Y, Z); \ @@ -289,9 +272,6 @@ void ws_close(CURL *curl); /* just close the connection */ } \ } while(0) -#define res_multi_remove_handle(A, B) \ - exe_multi_remove_handle(A, B, __FILE__, __LINE__) - #define chk_multi_remove_handle(A, B, Y, Z) \ do { \ exe_multi_remove_handle(A, B, Y, Z); \ @@ -425,9 +405,6 @@ void ws_close(CURL *curl); /* just close the connection */ } \ } while(0) -#define res_multi_poll(A, B, C, D, E) \ - exe_multi_poll(A, B, C, D, E, __FILE__, __LINE__) - #define chk_multi_poll(A, B, C, D, E, Y, Z) \ do { \ exe_multi_poll(A, B, C, D, E, Y, Z); \ @@ -455,27 +432,17 @@ void ws_close(CURL *curl); /* just close the connection */ #define res_multi_wakeup(A) \ exe_multi_wakeup(A, __FILE__, __LINE__) -#define chk_multi_wakeup(A, Y, Z) \ - do { \ - exe_multi_wakeup(A, Y, Z); \ - if(result) \ - goto test_cleanup; \ - } while(0) - -#define multi_wakeup(A) \ - chk_multi_wakeup(A, __FILE__, __LINE__) - /* ---------------------------------------------------------------- */ #define exe_select_test(A, B, C, D, E, Y, Z) \ do { \ if(select_wrapper(A, B, C, D, E) == -1) { \ - int ec = SOCKERRNO; \ - char ecbuf[STRERROR_LEN]; \ + int sockerr = SOCKERRNO; \ + char sockerrbuf[STRERROR_LEN]; \ curl_mfprintf(stderr, \ - "%s:%d select() failed, with " \ - "errno %d (%s)\n", \ - Y, Z, ec, curlx_strerror(ec, ecbuf, sizeof(ecbuf))); \ + "%s:%d select() failed, with errno %d (%s)\n", Y, Z, \ + sockerr, curlx_strerror(sockerr, sockerrbuf, \ + sizeof(sockerrbuf))); \ result = TEST_ERR_SELECT; \ } \ } while(0) @@ -517,9 +484,6 @@ void ws_close(CURL *curl); /* just close the connection */ #define res_test_timedout() \ exe_test_timedout(TEST_HANG_TIMEOUT, __FILE__, __LINE__) -#define res_test_timedout_custom(T) \ - exe_test_timedout(T, __FILE__, __LINE__) - #define chk_test_timedout(T, Y, Z) \ do { \ exe_test_timedout(T, Y, Z); \ @@ -533,23 +497,27 @@ void ws_close(CURL *curl); /* just close the connection */ #define abort_on_test_timeout_custom(T) \ chk_test_timedout(T, __FILE__, __LINE__) -/* ---------------------------------------------------------------- */ - -#define exe_global_init(A, Y, Z) \ - do { \ - CURLcode ec = curl_global_init(A); \ - if(ec != CURLE_OK) { \ - curl_mfprintf(stderr, \ - "%s:%d curl_global_init() failed, " \ - "with code %d (%s)\n", \ - Y, Z, ec, curl_easy_strerror(ec)); \ - result = ec; \ - } \ - } while(0) +#define NUM_HANDLES 4 /* global default */ #define res_global_init(A) \ exe_global_init(A, __FILE__, __LINE__) +#endif /* !UNITTESTS */ + +#if !defined(UNITTESTS) || defined(BUILDING_LIBCURL) + +/* ---------------------------------------------------------------- */ + +#define exe_global_init(A, Y, Z) \ + do { \ + result = curl_global_init(A); \ + if(result) \ + curl_mfprintf(stderr, \ + "%s:%d curl_global_init() failed, " \ + "with code %d (%s)\n", \ + Y, Z, (int)result, curl_easy_strerror(result)); \ + } while(0) + #define chk_global_init(A, Y, Z) \ do { \ exe_global_init(A, Y, Z); \ @@ -563,13 +531,6 @@ void ws_close(CURL *curl); /* just close the connection */ #define global_init(A) \ chk_global_init(A, __FILE__, __LINE__) -#define NO_SUPPORT_BUILT_IN \ - { \ - (void)URL; \ - curl_mfprintf(stderr, "Missing support\n"); \ - return CURLE_UNSUPPORTED_PROTOCOL; \ - } - -#define NUM_HANDLES 4 /* global default */ +#endif /* !UNITTESTS || BUILDING_LIBCURL */ #endif /* HEADER_LIBTEST_FIRST_H */ diff --git a/tests/libtest/lib1156.c b/tests/libtest/lib1156.c index fa75a6ae84b1..b8339c245613 100644 --- a/tests/libtest/lib1156.c +++ b/tests/libtest/lib1156.c @@ -92,22 +92,22 @@ static int onetest(CURL *curl, const char *url, const struct testparams *p, if(p->flags & F_HTTP416) replyselector += 2; curl_msnprintf(urlbuf, sizeof(urlbuf), "%s%04u", url, replyselector); - test_setopt(curl, CURLOPT_URL, urlbuf); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_RESUME_FROM, (p->flags & F_RESUME) ? 3L : 0L); - test_setopt(curl, CURLOPT_RANGE, !(p->flags & F_RESUME) ? + easy_setopt(curl, CURLOPT_URL, urlbuf); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_RESUME_FROM, (p->flags & F_RESUME) ? 3L : 0L); + easy_setopt(curl, CURLOPT_RANGE, !(p->flags & F_RESUME) ? "3-1000000" : (char *)NULL); - test_setopt(curl, CURLOPT_FAILONERROR, (p->flags & F_FAIL) ? 1L : 0L); + easy_setopt(curl, CURLOPT_FAILONERROR, (p->flags & F_FAIL) ? 1L : 0L); hasbody = 0; result = curl_easy_perform(curl); if(result != p->result) { curl_mprintf("%zu: bad error code (%d): resume=%s, fail=%s, http416=%s, " - "content-range=%s, expected=%d\n", num, result, + "content-range=%s, expected=%d\n", num, (int)result, (p->flags & F_RESUME) ? "yes" : "no", (p->flags & F_FAIL) ? "yes" : "no", (p->flags & F_HTTP416) ? "yes" : "no", (p->flags & F_CONTENTRANGE) ? "yes" : "no", - p->result); + (int)p->result); return 1; } if(hasbody && (p->flags & F_IGNOREBODY)) { @@ -149,7 +149,7 @@ static CURLcode test_lib1156(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_WRITEFUNCTION, writedata); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, writedata); #ifdef SINGLETEST if(SINGLETEST == i) diff --git a/tests/libtest/lib1485.c b/tests/libtest/lib1485.c index a4eb07a62802..46b985151325 100644 --- a/tests/libtest/lib1485.c +++ b/tests/libtest/lib1485.c @@ -47,10 +47,10 @@ static size_t t1485_header_callback(char *ptr, size_t size, size_t nmemb, /* end of a response */ result = curl_easy_getinfo(st->curl, CURLINFO_RESPONSE_CODE, &httpcode); curl_mfprintf(stderr, "header_callback, get status: %ld, %d\n", - httpcode, result); + httpcode, (int)result); if(httpcode < 100 || httpcode >= 1000) { curl_mfprintf(stderr, "header_callback, invalid status: %ld, %d\n", - httpcode, result); + httpcode, (int)result); return CURLE_WRITE_ERROR; } st->http_status = (int)httpcode; @@ -58,7 +58,7 @@ static size_t t1485_header_callback(char *ptr, size_t size, size_t nmemb, result = curl_easy_getinfo(st->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &clen); curl_mfprintf(stderr, "header_callback, info Content-Length: " - "%" CURL_FORMAT_CURL_OFF_T ", %d\n", clen, result); + "%" CURL_FORMAT_CURL_OFF_T ", %d\n", clen, (int)result); if(result) { st->result = result; return CURLE_WRITE_ERROR; diff --git a/tests/libtest/lib1507.c b/tests/libtest/lib1507.c index 67ccce00ee5b..d125a4975bc9 100644 --- a/tests/libtest/lib1507.c +++ b/tests/libtest/lib1507.c @@ -105,8 +105,8 @@ static CURLcode test_lib1507(const char *URL) /* In a real-world program you OF COURSE check the return code of the function calls. On success, the value of maxfd is guaranteed to be greater or equal than -1. We call select(maxfd + 1, ...), specially in - case of (maxfd == -1), we call select(0, ...), which is basically equal - to sleep. */ + case of (maxfd == -1), we call select(0, ...), which is equal to sleep. + */ rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout); diff --git a/tests/libtest/lib1509.c b/tests/libtest/lib1509.c index 1099c3c956f3..95258d678f5d 100644 --- a/tests/libtest/lib1509.c +++ b/tests/libtest/lib1509.c @@ -66,7 +66,7 @@ static CURLcode test_lib1509(const char *URL) if(code != CURLE_OK) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed, " "with code %d (%s)\n", - __FILE__, __LINE__, code, curl_easy_strerror(code)); + __FILE__, __LINE__, (int)code, curl_easy_strerror(code)); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } @@ -75,7 +75,7 @@ static CURLcode test_lib1509(const char *URL) if(code != CURLE_OK) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed, " "with code %d (%s)\n", - __FILE__, __LINE__, code, curl_easy_strerror(code)); + __FILE__, __LINE__, (int)code, curl_easy_strerror(code)); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } diff --git a/tests/libtest/lib1514.c b/tests/libtest/lib1514.c index 7a7a76fff0ba..a799e7668de4 100644 --- a/tests/libtest/lib1514.c +++ b/tests/libtest/lib1514.c @@ -41,13 +41,13 @@ static size_t t1514_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) return 0; if(pooh->sizeleft) { - *ptr = pooh->readptr[0]; /* copy one single byte */ - pooh->readptr++; /* advance pointer */ - pooh->sizeleft--; /* less data left */ - return 1; /* we return 1 byte at a time! */ + *ptr = pooh->readptr[0]; /* copy one single byte */ + pooh->readptr++; /* advance pointer */ + pooh->sizeleft--; /* less data left */ + return 1; /* we return 1 byte at a time! */ } - return 0; /* no more data left to deliver */ + return 0; /* no more data left to deliver */ } static CURLcode test_lib1514(const char *URL) diff --git a/tests/libtest/lib1515.c b/tests/libtest/lib1515.c index dae7acce506d..f9bacf5c6033 100644 --- a/tests/libtest/lib1515.c +++ b/tests/libtest/lib1515.c @@ -128,7 +128,8 @@ static CURLcode test_lib1515(const char *URL) /* second request must succeed like the first one */ result = do_one_request(multi, target_url, dns_entry); if(result != CURLE_OK) { - curl_mfprintf(stderr, "request %s failed with %d\n", target_url, result); + curl_mfprintf(stderr, "request %s failed with %d\n", target_url, + (int)result); goto test_cleanup; } diff --git a/tests/libtest/lib1517.c b/tests/libtest/lib1517.c index 57b0a0d5a5b9..30e80a7bceb7 100644 --- a/tests/libtest/lib1517.c +++ b/tests/libtest/lib1517.c @@ -33,8 +33,8 @@ static size_t t1517_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) struct t1517_WriteThis *pooh = (struct t1517_WriteThis *)userp; size_t tocopy = size * nmemb; - /* Wait one second before return POST data * - * so libcurl will wait before sending request body */ + /* Wait one second before return POST data + so libcurl waits before sending request body */ curlx_wait_ms(1000); if(tocopy < 1 || !pooh->sizeleft) @@ -43,9 +43,9 @@ static size_t t1517_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) if(pooh->sizeleft < tocopy) tocopy = pooh->sizeleft; - memcpy(ptr, pooh->readptr, tocopy);/* copy requested data */ - pooh->readptr += tocopy; /* advance pointer */ - pooh->sizeleft -= tocopy; /* less data left */ + memcpy(ptr, pooh->readptr, tocopy); /* copy requested data */ + pooh->readptr += tocopy; /* advance pointer */ + pooh->sizeleft -= tocopy; /* less data left */ return tocopy; } @@ -60,7 +60,7 @@ static CURLcode test_lib1517(const char *URL) struct t1517_WriteThis pooh; pooh.readptr = testdata; - pooh.sizeleft = strlen(testdata); + pooh.sizeleft = sizeof(testdata) - 1; if(curl_global_init(CURL_GLOBAL_ALL)) { curl_mfprintf(stderr, "curl_global_init() failed\n"); @@ -75,32 +75,32 @@ static CURLcode test_lib1517(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t1517_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t1517_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, &pooh); + easy_setopt(curl, CURLOPT_READDATA, &pooh); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); #if 0 /* detect HTTP error codes >= 400 */ - test_setopt(curl, CURLOPT_FAILONERROR, 1L); + easy_setopt(curl, CURLOPT_FAILONERROR, 1L); #endif - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib1518.c b/tests/libtest/lib1518.c index f6fe44c5e94c..09b85148191c 100644 --- a/tests/libtest/lib1518.c +++ b/tests/libtest/lib1518.c @@ -25,16 +25,6 @@ /* Test inspired by github issue 3340 */ -static size_t t1518_write_cb(char *buffer, size_t size, size_t nitems, - void *outstream) -{ - (void)buffer; - (void)size; - (void)nitems; - (void)outstream; - return 0; -} - static CURLcode test_lib1518(const char *URL) { CURL *curl; @@ -59,16 +49,16 @@ static CURLcode test_lib1518(const char *URL) if(!urlu || rc) { goto test_cleanup; } - test_setopt(curl, CURLOPT_CURLU, urlu); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_CURLU, urlu); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); } else { - test_setopt(curl, CURLOPT_URL, URL); - /* just to make it explicit and visible in this test: */ - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); + easy_setopt(curl, CURLOPT_URL, URL); + /* to make it explicit and visible in this test: */ + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); } - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -77,14 +67,14 @@ static CURLcode test_lib1518(const char *URL) curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &curlRedirectCount); curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effectiveUrl); curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &redirectUrl); - test_setopt(curl, CURLOPT_WRITEFUNCTION, t1518_write_cb); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); curl_mprintf("result %d\n" "status %ld\n" "redirects %ld\n" "effectiveurl %s\n" "redirecturl %s\n", - result, + (int)result, curlResponseCode, curlRedirectCount, effectiveUrl, diff --git a/tests/libtest/lib1520.c b/tests/libtest/lib1520.c index a76c64a5a91d..9ed257591ee0 100644 --- a/tests/libtest/lib1520.c +++ b/tests/libtest/lib1520.c @@ -87,13 +87,13 @@ static CURLcode test_lib1520(const char *URL) /* more addresses can be added here */ rcpt_list = curl_slist_append(rcpt_list, ""); #endif - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1520_read_cb); - test_setopt(curl, CURLOPT_READDATA, &upload_ctx); - test_setopt(curl, CURLOPT_MAIL_FROM, ""); - test_setopt(curl, CURLOPT_MAIL_RCPT, rcpt_list); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1520_read_cb); + easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); + easy_setopt(curl, CURLOPT_MAIL_FROM, ""); + easy_setopt(curl, CURLOPT_MAIL_RCPT, rcpt_list); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1522.c b/tests/libtest/lib1522.c index 56a6992bdb41..9f6dc335154d 100644 --- a/tests/libtest/lib1522.c +++ b/tests/libtest/lib1522.c @@ -61,9 +61,9 @@ static CURLcode test_lib1522(const char *URL) debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Remove "Expect: 100-continue" */ pHeaderList = curl_slist_append(pHeaderList, "Expect:"); @@ -87,7 +87,7 @@ static CURLcode test_lib1522(const char *URL) } } else { - curl_mprintf("curl_easy_perform() failed. e = %d\n", code); + curl_mprintf("curl_easy_perform() failed. e = %d\n", (int)code); } test_cleanup: curl_slist_free_all(pHeaderList); diff --git a/tests/libtest/lib1523.c b/tests/libtest/lib1523.c index 47254ffcdf14..53ed580a59a5 100644 --- a/tests/libtest/lib1523.c +++ b/tests/libtest/lib1523.c @@ -36,14 +36,6 @@ static int dload_progress_cb(void *a, curl_off_t b, curl_off_t c, return 0; } -static size_t t1523_write_cb(char *d, size_t n, size_t l, void *p) -{ - /* take care of the data here, ignored in this example */ - (void)d; - (void)p; - return n * l; -} - static CURLcode run(CURL *curl, long limit, long time) { curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, limit); @@ -59,18 +51,18 @@ static CURLcode test_lib1523(const char *URL) curl_global_init(CURL_GLOBAL_ALL); curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, URL); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, t1523_write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, dload_progress_cb); result = run(curl, 1, 2); if(result) - curl_mfprintf(stderr, "error (%d) %s\n", result, buffer); + curl_mfprintf(stderr, "error (%d) %s\n", (int)result, buffer); result = run(curl, 12000, 1); if(result != CURLE_OPERATION_TIMEDOUT) - curl_mfprintf(stderr, "error (%d) %s\n", result, buffer); + curl_mfprintf(stderr, "error (%d) %s\n", (int)result, buffer); else result = CURLE_OK; diff --git a/tests/libtest/lib1525.c b/tests/libtest/lib1525.c index 29ce7a9a0bd0..3498941ea2ee 100644 --- a/tests/libtest/lib1525.c +++ b/tests/libtest/lib1525.c @@ -24,7 +24,7 @@ ***************************************************************************/ /* - * This unit test PUT http data over proxy. Proxy header will be different + * This unit test PUT http data over proxy. Proxy header is different * from server http header */ @@ -69,20 +69,20 @@ static CURLcode test_lib1525(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_PROXYHEADER, hhl); - test_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED); - test_setopt(curl, CURLOPT_POST, 0L); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - test_setopt(curl, CURLOPT_READFUNCTION, t1525_read_cb); - test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, (long)t1525_datalen); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_PROXYHEADER, hhl); + easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED); + easy_setopt(curl, CURLOPT_POST, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + easy_setopt(curl, CURLOPT_READFUNCTION, t1525_read_cb); + easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)t1525_datalen); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1526.c b/tests/libtest/lib1526.c index 30c4f552359d..6e01804628cc 100644 --- a/tests/libtest/lib1526.c +++ b/tests/libtest/lib1526.c @@ -23,7 +23,7 @@ ***************************************************************************/ /* - * This unit test PUT http data over proxy. Proxy header will be different + * This unit test PUT http data over proxy. Proxy header is different * from server http header */ @@ -73,20 +73,20 @@ static CURLcode test_lib1526(const char *URL) } phl = tmp; - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_PROXYHEADER, phl); - test_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); - test_setopt(curl, CURLOPT_POST, 0L); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - test_setopt(curl, CURLOPT_READFUNCTION, t1526_read_cb); - test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, (long)t1526_datalen); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_PROXYHEADER, phl); + easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); + easy_setopt(curl, CURLOPT_POST, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + easy_setopt(curl, CURLOPT_READFUNCTION, t1526_read_cb); + easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)t1526_datalen); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1527.c b/tests/libtest/lib1527.c index efa39efb7369..ef438dc901ae 100644 --- a/tests/libtest/lib1527.c +++ b/tests/libtest/lib1527.c @@ -23,7 +23,7 @@ ***************************************************************************/ /* - * This unit test PUT http data over proxy. Same http header will be generated + * This unit test PUT http data over proxy. Same http header is generated * for server and proxy */ @@ -72,19 +72,19 @@ static CURLcode test_lib1527(const char *URL) } hhl = tmp; - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_POST, 0L); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - test_setopt(curl, CURLOPT_READFUNCTION, t1527_read_cb); - test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, (long)t1527_datalen); - test_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_POST, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + easy_setopt(curl, CURLOPT_READFUNCTION, t1527_read_cb); + easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)t1527_datalen); + easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_UNIFIED); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1528.c b/tests/libtest/lib1528.c index d5c6030222a6..f8db6725353b 100644 --- a/tests/libtest/lib1528.c +++ b/tests/libtest/lib1528.c @@ -50,14 +50,14 @@ static CURLcode test_lib1528(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_PROXYHEADER, phl); - test_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_PROXYHEADER, phl); + easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1529.c b/tests/libtest/lib1529.c index df83e54eb7d1..ccf003203598 100644 --- a/tests/libtest/lib1529.c +++ b/tests/libtest/lib1529.c @@ -43,11 +43,11 @@ static CURLcode test_lib1529(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, bURL); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, bURL); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1530.c b/tests/libtest/lib1530.c index 39ee30b6b805..ecce16803d1f 100644 --- a/tests/libtest/lib1530.c +++ b/tests/libtest/lib1530.c @@ -52,9 +52,9 @@ static CURLcode test_lib1530(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, "http://99.99.99.99:9999"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket); + easy_setopt(curl, CURLOPT_URL, "http://99.99.99.99:9999"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, opensocket); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1531.c b/tests/libtest/lib1531.c index f8c9071e3b8b..def737f9f10e 100644 --- a/tests/libtest/lib1531.c +++ b/tests/libtest/lib1531.c @@ -128,7 +128,7 @@ static CURLcode test_lib1531(const char *URL) msg = curl_multi_info_read(multi, &msgs_left); if(msg && msg->msg == CURLMSG_DONE) { curl_mprintf("HTTP transfer completed with status %d\n", - msg->data.result); + (int)msg->data.result); break; } diff --git a/tests/libtest/lib1532.c b/tests/libtest/lib1532.c index 533abb3e7892..25c5ab439a3a 100644 --- a/tests/libtest/lib1532.c +++ b/tests/libtest/lib1532.c @@ -41,7 +41,7 @@ static CURLcode test_lib1532(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -49,7 +49,7 @@ static CURLcode test_lib1532(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(httpcode != 200) { @@ -66,7 +66,7 @@ static CURLcode test_lib1532(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(httpcode) { diff --git a/tests/libtest/lib1533.c b/tests/libtest/lib1533.c index 6e7d1a471b67..c0ad8e46cb9a 100644 --- a/tests/libtest/lib1533.c +++ b/tests/libtest/lib1533.c @@ -98,7 +98,7 @@ static CURLcode perform_and_check_connections(CURL *curl, result = curl_easy_perform(curl); if(result != CURLE_OK) { - curl_mfprintf(stderr, "curl_easy_perform() failed with %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() failed with %d\n", (int)result); return TEST_ERR_MAJOR_BAD; } @@ -139,15 +139,15 @@ static CURLcode test_lib1533(const char *URL) reset_data(&data, curl); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)data.remaining_bytes); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1533_read_cb); - test_setopt(curl, CURLOPT_READDATA, &data); - test_setopt(curl, CURLOPT_WRITEFUNCTION, t1533_write_cb); - test_setopt(curl, CURLOPT_WRITEDATA, &data); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1533_read_cb); + easy_setopt(curl, CURLOPT_READDATA, &data); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, t1533_write_cb); + easy_setopt(curl, CURLOPT_WRITEDATA, &data); result = perform_and_check_connections(curl, @@ -165,7 +165,7 @@ static CURLcode test_lib1533(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_KEEP_SENDING_ON_ERROR, 1L); + easy_setopt(curl, CURLOPT_KEEP_SENDING_ON_ERROR, 1L); reset_data(&data, curl); diff --git a/tests/libtest/lib1534.c b/tests/libtest/lib1534.c index e672d585ee11..9c4491b8bcef 100644 --- a/tests/libtest/lib1534.c +++ b/tests/libtest/lib1534.c @@ -42,7 +42,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(filetime != -1) { @@ -60,7 +60,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -71,7 +71,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(filetime != 30) { @@ -97,7 +97,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(filetime != -1) { @@ -117,7 +117,7 @@ static CURLcode test_lib1534(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(filetime != -1) { diff --git a/tests/libtest/lib1535.c b/tests/libtest/lib1535.c index aae63fc9fa71..68ec25fb60f4 100644 --- a/tests/libtest/lib1535.c +++ b/tests/libtest/lib1535.c @@ -43,7 +43,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(protocol) { @@ -60,7 +60,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -72,7 +72,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(protocol != CURLPROTO_HTTP) { @@ -100,7 +100,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(protocol) { @@ -121,7 +121,7 @@ static CURLcode test_lib1535(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(protocol) { diff --git a/tests/libtest/lib1536.c b/tests/libtest/lib1536.c index 9debd78807f7..f6632c736106 100644 --- a/tests/libtest/lib1536.c +++ b/tests/libtest/lib1536.c @@ -42,7 +42,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(scheme) { @@ -59,7 +59,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -70,14 +70,13 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } - if(!scheme || memcmp(scheme, "http", 5) != 0) { + if(!scheme || memcmp(scheme, "http", 5)) { curl_mfprintf(stderr, "%s:%d scheme of http resource is incorrect; " "expected 'http' but is %s\n", - __FILE__, __LINE__, - (scheme == NULL ? "NULL" : "invalid")); + __FILE__, __LINE__, scheme ? "invalid" : "NULL"); result = CURLE_HTTP_RETURNED_ERROR; goto test_cleanup; } @@ -97,7 +96,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(scheme) { @@ -116,7 +115,7 @@ static CURLcode test_lib1536(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } if(scheme) { diff --git a/tests/libtest/lib1537.c b/tests/libtest/lib1537.c index c9668c0ddf10..1ef0be9c7522 100644 --- a/tests/libtest/lib1537.c +++ b/tests/libtest/lib1537.c @@ -77,7 +77,7 @@ static CURLcode test_lib1537(const char *URL) curl_mprintf("escape -1 length: %s\n", ptr); /* weird input length */ - outlen = 2017; /* just a value */ + outlen = 2017; /* an arbitrary value */ ptr = curl_easy_unescape(NULL, "moahahaha", -1, &outlen); curl_mprintf("unescape -1 length: %s %d\n", ptr, outlen); diff --git a/tests/libtest/lib1538.c b/tests/libtest/lib1538.c index 6a1db4e93e46..6433e571b766 100644 --- a/tests/libtest/lib1538.c +++ b/tests/libtest/lib1538.c @@ -43,17 +43,16 @@ static CURLcode test_lib1538(const char *URL) curl_url_strerror((CURLUcode)-INT_MAX); /* NOLINTEND(clang-analyzer-optin.core.EnumCastOutOfRange) */ for(easyret = CURLE_OK; easyret <= CURL_LAST; easyret++) { - curl_mprintf("e%d: %s\n", easyret, curl_easy_strerror(easyret)); + curl_mprintf("e%d: %s\n", (int)easyret, curl_easy_strerror(easyret)); } - for(mresult = CURLM_CALL_MULTI_PERFORM; mresult <= CURLM_LAST; - mresult++) { + for(mresult = CURLM_CALL_MULTI_PERFORM; mresult <= CURLM_LAST; mresult++) { curl_mprintf("m%d: %s\n", mresult, curl_multi_strerror(mresult)); } for(shareret = CURLSHE_OK; shareret <= CURLSHE_LAST; shareret++) { - curl_mprintf("s%d: %s\n", shareret, curl_share_strerror(shareret)); + curl_mprintf("s%d: %s\n", (int)shareret, curl_share_strerror(shareret)); } for(urlret = CURLUE_OK; urlret <= CURLUE_LAST; urlret++) { - curl_mprintf("u%d: %s\n", urlret, curl_url_strerror(urlret)); + curl_mprintf("u%d: %s\n", (int)urlret, curl_url_strerror(urlret)); } return result; diff --git a/tests/libtest/lib1540.c b/tests/libtest/lib1540.c index ee18046f4270..b4fc49eb2be4 100644 --- a/tests/libtest/lib1540.c +++ b/tests/libtest/lib1540.c @@ -107,7 +107,7 @@ static CURLcode test_lib1540(const char *URL) debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); diff --git a/tests/libtest/lib1541.c b/tests/libtest/lib1541.c index 2dc0d2cd2611..d6888dbb63a4 100644 --- a/tests/libtest/lib1541.c +++ b/tests/libtest/lib1541.c @@ -34,7 +34,7 @@ struct t1541_transfer_status { static void t1541_geterr(const char *name, CURLcode val, int lineno) { curl_mprintf("CURLINFO_%s returned %d, \"%s\" on line %d\n", - name, val, curl_easy_strerror(val), lineno); + name, (int)val, curl_easy_strerror(val), lineno); } static void report_time(const char *key, const char *where, curl_off_t time, diff --git a/tests/libtest/lib1549.c b/tests/libtest/lib1549.c index ce5d70233133..e004546f58fe 100644 --- a/tests/libtest/lib1549.c +++ b/tests/libtest/lib1549.c @@ -42,9 +42,9 @@ static CURLcode test_lib1549(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_COOKIEFILE, ""); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_COOKIEFILE, ""); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1553.c b/tests/libtest/lib1553.c index dd236cf73b58..8aa2b1a1534c 100644 --- a/tests/libtest/lib1553.c +++ b/tests/libtest/lib1553.c @@ -72,7 +72,7 @@ static CURLcode test_lib1553(const char *URL) debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); diff --git a/tests/libtest/lib1554.c b/tests/libtest/lib1554.c index 4b4b07cb0c41..8b37374c5aa3 100644 --- a/tests/libtest/lib1554.c +++ b/tests/libtest/lib1554.c @@ -72,8 +72,8 @@ static CURLcode test_lib1554(const char *URL) curl_share_setopt(share, CURLSHOPT_LOCKFUNC, t1554_test_lock); curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, t1554_test_unlock); - /* Loop the transfer and cleanup the handle properly every lap. This will - still reuse connections since the pool is in the shared object! */ + /* Loop the transfer and cleanup the handle properly every lap. This + still reuses connections since the pool is in the shared object! */ for(i = 0; i < 3; i++) { CURL *curl = curl_easy_init(); @@ -83,7 +83,7 @@ static CURLcode test_lib1554(const char *URL) /* use the share object */ curl_easy_setopt(curl, CURLOPT_SHARE, share); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); /* always cleanup */ diff --git a/tests/libtest/lib1555.c b/tests/libtest/lib1555.c index 4932fd24f7cd..c6c806426a05 100644 --- a/tests/libtest/lib1555.c +++ b/tests/libtest/lib1555.c @@ -44,9 +44,9 @@ static int progressCallback(void *arg, (void)ultotal; (void)ulnow; result = curl_easy_recv(t1555_curl, buffer, 256, &n); - curl_mprintf("curl_easy_recv returned %d\n", result); + curl_mprintf("curl_easy_recv returned %d\n", (int)result); result = curl_easy_send(t1555_curl, buffer, n, &n); - curl_mprintf("curl_easy_send returned %d\n", result); + curl_mprintf("curl_easy_send returned %d\n", (int)result); return 1; } diff --git a/tests/libtest/lib1556.c b/tests/libtest/lib1556.c index c91161628fa7..201ea4b15962 100644 --- a/tests/libtest/lib1556.c +++ b/tests/libtest/lib1556.c @@ -60,7 +60,7 @@ static CURLcode test_lib1556(const char *URL) if(code != CURLE_OK) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed, " "with code %d (%s)\n", - __FILE__, __LINE__, code, curl_easy_strerror(code)); + __FILE__, __LINE__, (int)code, curl_easy_strerror(code)); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } diff --git a/tests/libtest/lib1557.c b/tests/libtest/lib1557.c index 60662aac809f..e94941934ad9 100644 --- a/tests/libtest/lib1557.c +++ b/tests/libtest/lib1557.c @@ -48,7 +48,7 @@ static CURLcode test_lib1557(const char *URL) multi_remove_handle(multi, curl2); - /* If curl2 is still in the connect-pending list, this will crash */ + /* If curl2 is still in the connect-pending list, this crashes */ multi_remove_handle(multi, curl1); test_cleanup: diff --git a/tests/libtest/lib1558.c b/tests/libtest/lib1558.c index 4077cb8eb1f5..0832f8b1c5ee 100644 --- a/tests/libtest/lib1558.c +++ b/tests/libtest/lib1558.c @@ -36,18 +36,18 @@ static CURLcode test_lib1558(const char *URL) result = curl_easy_perform(curl); if(result) { curl_mfprintf(stderr, "curl_easy_perform() returned %d (%s)\n", - result, curl_easy_strerror(result)); + (int)result, curl_easy_strerror(result)); goto test_cleanup; } result = curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol); if(result) { curl_mfprintf(stderr, "curl_easy_getinfo() returned %d (%s)\n", - result, curl_easy_strerror(result)); + (int)result, curl_easy_strerror(result)); goto test_cleanup; } - curl_mprintf("Protocol: %lx\n", protocol); + curl_mprintf("Protocol: %lx\n", (unsigned long)protocol); curl_easy_cleanup(curl); curl_global_cleanup(); diff --git a/tests/libtest/lib1559.c b/tests/libtest/lib1559.c index 4cf3953b4fe2..951b5db234ce 100644 --- a/tests/libtest/lib1559.c +++ b/tests/libtest/lib1559.c @@ -47,23 +47,23 @@ static CURLcode test_lib1559(const char *URL) result = curl_easy_setopt(curl, CURLOPT_URL, longurl); curl_mprintf("CURLOPT_URL %d bytes URL == %d\n", - EXCESSIVE, result); + EXCESSIVE, (int)result); result = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, longurl); curl_mprintf("CURLOPT_POSTFIELDS %d bytes data == %d\n", - EXCESSIVE, result); + EXCESSIVE, (int)result); u = curl_url(); if(u) { CURLUcode uc = curl_url_set(u, CURLUPART_URL, longurl, 0); curl_mprintf("CURLUPART_URL %d bytes URL == %d (%s)\n", - EXCESSIVE, uc, curl_url_strerror(uc)); + EXCESSIVE, (int)uc, curl_url_strerror(uc)); uc = curl_url_set(u, CURLUPART_SCHEME, longurl, CURLU_NON_SUPPORT_SCHEME); curl_mprintf("CURLUPART_SCHEME %d bytes scheme == %d (%s)\n", - EXCESSIVE, uc, curl_url_strerror(uc)); + EXCESSIVE, (int)uc, curl_url_strerror(uc)); uc = curl_url_set(u, CURLUPART_USER, longurl, 0); curl_mprintf("CURLUPART_USER %d bytes user == %d (%s)\n", - EXCESSIVE, uc, curl_url_strerror(uc)); + EXCESSIVE, (int)uc, curl_url_strerror(uc)); curl_url_cleanup(u); } diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c index 6d42b3569478..d4274bfb3e40 100644 --- a/tests/libtest/lib1560.c +++ b/tests/libtest/lib1560.c @@ -27,7 +27,7 @@ * * Since the URL parser by default only accepts schemes that *this instance* * of libcurl supports, make sure that the test1560 file lists all the schemes - * that this test will assume to be present! + * that this test assumes to be present! */ #include "first.h" @@ -76,7 +76,7 @@ static int checkparts(CURLU *u, const char *in, const char *wanted, z ? " " : "", z ? z : ""); } else - curl_msnprintf(bufp, len, "%s[%d]", buf[0] ? " | " : "", rc); + curl_msnprintf(bufp, len, "%s[%d]", buf[0] ? " | " : "", (int)rc); n = strlen(bufp); bufp += n; @@ -196,20 +196,19 @@ static const struct testcase get_parts_list[] = { "http://host:00080/", "http | [11] | [12] | [13] | host | 80 | / | [16] | [17]", 0, 0, CURLUE_OK }, - { /* Single dot host - technically valid in some contexts but often - rejected */ + { /* Single dot host - not ok */ "http://./", - "http | [11] | [12] | [13] | . | [15] | / | [16] | [17]", - 0, 0, CURLUE_OK }, + "", + 0, 0, CURLUE_BAD_HOSTNAME }, { /* Host starting with a dash (RFC 1123 technically allows it, but many - parsers don't) */ + parsers do not) */ "http://-atest/", "http | [11] | [12] | [13] | -atest | [15] | / | [16] | [17]", 0, 0, CURLUE_OK }, - { /* Multiple trailing dots, not okay in DNS but works in /etc/hosts */ + { /* Multiple trailing dots is not okay */ "http://example.com../", - "http | [11] | [12] | [13] | example.com.. | [15] | / | [16] | [17]", - 0, 0, CURLUE_OK }, + "", + 0, 0, CURLUE_BAD_HOSTNAME }, { /* Empty IPv6 Zone ID */ "http://[fe80::1%]/", "", 0, 0, CURLUE_BAD_IPV6 }, @@ -327,6 +326,7 @@ static const struct testcase get_parts_list[] = { {"https://user@example.net?he l lo", "https | user | [12] | [13] | example.net | [15] | / | he l lo | [17]", CURLU_ALLOW_SPACE, 0, CURLUE_OK}, + {"https://exam|ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://exam{}[]ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://exam{ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"https://exam}ple.net", "", 0, 0, CURLUE_BAD_HOSTNAME}, @@ -626,6 +626,86 @@ static const struct testcase get_parts_list[] = { }; static const struct urltestcase get_url_list[] = { + /* percent-encoded IP addresses */ + {"https://127.0.0.%31.", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://127.0.0.0%78f%46.", "https://127.0.0.255/", 0, 0, CURLUE_OK}, + {"https://%30%31%37%37%2e%31", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://[fe80%3A%3A20c%3A29ff%3Afe9c%3A409b]/", + "https://[fe80::20c:29ff:fe9c:409b]/", 0, 0, CURLUE_OK }, + + /* IPvFuture format */ + {"http://[v1.fe80::abcd]/", "", 0, 0, CURLUE_BAD_IPV6}, + + /* trailing dot on valid host */ + {"http://example.com./", "http://example.com./", 0, 0, CURLUE_OK}, + + /* the exact upper valid port boundary */ + {"http://host:65535/", "http://host:65535/", 0, 0, CURLUE_OK}, + + /* Internationalized path (not host). */ + {"https://example.com/r\xc3\xa4ksm\xc3\xb6rg\xc3\xa5s", + "https://example.com/r%C3%A4ksm%C3%B6rg%C3%A5s", + CURLU_URLENCODE, 0, CURLUE_OK}, + + /* weird fragments */ + {"http://host/#a#b", "http://host/#a#b", 0, 0, CURLUE_OK}, + + /* Empty query parameter values */ + {"http://host/?a=", "http://host/?a=", 0, 0, CURLUE_OK}, + {"http://host/?a=&b=", "http://host/?a=&b=", 0, 0, CURLUE_OK}, + + /* Percent-encoded userinfo */ + {"https://user%20name@example.com/", "https://user%20name@example.com/", + 0, 0, CURLUE_OK}, + {"https://user:pa%3Ass@example.com/", "https://user:pa%3Ass@example.com/", + 0, 0, CURLUE_OK}, + + /* malformed unbracketed IPv6 */ + {"https://fe80:8080::1/", "", 0, 0, CURLUE_BAD_PORT_NUMBER}, + {"https://::1/", "", 0, 0, CURLUE_BAD_PORT_NUMBER}, + + /* Empty host with standard schemes */ + {"http:///", "", 0, 0, CURLUE_NO_HOST}, + {"https://?q=1", "", 0, 0, CURLUE_NO_HOST}, + + /* Empty path segment normalization */ + {"http://example.com//", "http://example.com//", 0, 0, CURLUE_OK}, + {"http://example.com///foo", "http://example.com///foo", 0, 0, CURLUE_OK}, + + /* Empty user and password combinations */ + {"http://@example.com/", "http://@example.com/", 0, 0, CURLUE_OK}, + {"http://user:@example.com/", "http://user:@example.com/", 0, 0, CURLUE_OK}, + {"http://:password@example.com/", "http://:password@example.com/", + 0, 0, CURLUE_OK}, + + {"https://127.1.0x", "https://127.1.0x/", 0, 0, CURLUE_OK}, + {"https://127.0x", "https://127.0x/", 0, 0, CURLUE_OK}, + {"https://127.0x.1", "https://127.0x.1/", 0, 0, CURLUE_OK}, + {"https://127.1.1.0x", "https://127.1.1.0x/", 0, 0, CURLUE_OK}, + {"https://127.1.", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://127.1.:443", "https://127.0.0.1:443/", 0, 0, CURLUE_OK}, + {"https://127.1.?moo", "https://127.0.0.1/?moo", 0, 0, CURLUE_OK}, + {"https://127.1.#moo", "https://127.0.0.1/#moo", 0, 0, CURLUE_OK}, + {"https://127.1.a", "https://127.1.a/", 0, 0, CURLUE_OK}, + {"https://127.1..", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.1..:443", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.1..?moo", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.1..#moo", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.1.1.", "https://127.1.0.1/", 0, 0, CURLUE_OK}, + {"https://127.1.1./foo", "https://127.1.0.1/foo", 0, 0, CURLUE_OK}, + {"https://127.1.1.1.", "https://127.1.1.1/", 0, 0, CURLUE_OK}, + {"https://127.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://127.0.0.1.", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://127.0.0.0xff.", "https://127.0.0.255/", 0, 0, CURLUE_OK}, + {"https://127.0.0.1..", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"https://127.0.0.256..", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://hej./", "http://hej./", 0, 0, CURLUE_OK}, + {"http://hej../", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://hej.../", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://hej..../index.html", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://.", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://..", "", 0, 0, CURLUE_BAD_HOSTNAME}, + {"http://...", "", 0, 0, CURLUE_BAD_HOSTNAME}, {"018.0.0.0", "http://018.0.0.0/", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, {"08", "http://08/", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, {"0", "http://0.0.0.0/", CURLU_GUESS_SCHEME, 0, CURLUE_OK}, @@ -678,6 +758,7 @@ static const struct urltestcase get_url_list[] = { {"https://0xffffffff", "https://255.255.255.255/", 0, 0, CURLUE_OK}, {"https://1.0x1000000", "https://1.0x1000000/", 0, 0, CURLUE_OK}, {"https://0x7f.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, + {"https://0X7F.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, {"https://1.2.3.256.com", "https://1.2.3.256.com/", 0, 0, CURLUE_OK}, {"https://10.com", "https://10.com/", 0, 0, CURLUE_OK}, {"https://1.2.com", "https://1.2.com/", 0, 0, CURLUE_OK}, @@ -737,12 +818,14 @@ static const struct urltestcase get_url_list[] = { {"https://16843009", "https://1.1.1.1/", 0, 0, CURLUE_OK}, {"https://0177.1", "https://127.0.0.1/", 0, 0, CURLUE_OK}, {"https://0111.02.0x3", "https://73.2.0.3/", 0, 0, CURLUE_OK}, - {"https://0111.02.0x3.", "https://0111.02.0x3./", 0, 0, CURLUE_OK}, + {"https://0111.02.0X3", "https://73.2.0.3/", 0, 0, CURLUE_OK}, + {"https://0111.02.0x3.", "https://73.2.0.3/", 0, 0, CURLUE_OK}, {"https://0111.02.030", "https://73.2.0.24/", 0, 0, CURLUE_OK}, - {"https://0111.02.030.", "https://0111.02.030./", 0, 0, CURLUE_OK}, + {"https://0111.02.030.", "https://73.2.0.24/", 0, 0, CURLUE_OK}, {"https://0xff.0xff.0377.255", "https://255.255.255.255/", 0, 0, CURLUE_OK}, + {"https://0XFF.0XFF.0377.255", "https://255.255.255.255/", 0, 0, CURLUE_OK}, {"https://1.0xffffff", "https://1.255.255.255/", 0, 0, CURLUE_OK}, - /* IPv4 numerical overflows or syntax errors will not normalize */ + /* IPv4 numerical overflows or syntax errors do not normalize */ {"https://a127.0.0.1", "https://a127.0.0.1/", 0, 0, CURLUE_OK}, {"https://\xff.127.0.0.1", "https://%FF.127.0.0.1/", 0, CURLU_URLENCODE, CURLUE_OK}, @@ -766,8 +849,8 @@ static const struct urltestcase get_url_list[] = { "", 0, 0, CURLUE_BAD_IPV6}, {"https://[fe80::20c:29ff:fe9c:409b%25]:1234", - "https://[fe80::20c:29ff:fe9c:409b%2525]:1234/", - 0, 0, CURLUE_OK}, + "", + 0, 0, CURLUE_BAD_IPV6}, {"https://[fe80::20c:29ff:fe9c:409b%eth0]:1234", "https://[fe80::20c:29ff:fe9c:409b%25eth0]:1234/", 0, 0, CURLUE_OK}, @@ -858,7 +941,11 @@ static const struct urltestcase get_url_list[] = { {"file:///.", "file:///", 0, 0, CURLUE_OK}, {"file:///./", "file:///", 0, 0, CURLUE_OK}, {"file:///a", "file:///a", 0, 0, CURLUE_OK}, - {"file:./", "file://", 0, 0, CURLUE_OK}, + {"file:./", "", 0, 0, CURLUE_BAD_FILE_URL}, + {"file:foo", "", 0, 0, CURLUE_BAD_FILE_URL}, + {"file:foo/bar", "", 0, 0, CURLUE_BAD_FILE_URL}, + {"file:?q", "", 0, 0, CURLUE_BAD_FILE_URL}, + {"file:#f", "", 0, 0, CURLUE_BAD_FILE_URL}, {"http://example.com/hello/../here", "http://example.com/hello/../here", CURLU_PATH_AS_IS, 0, CURLUE_OK}, @@ -983,7 +1070,7 @@ static const struct setcase set_parts_list[] = { "https://example.com/one%20/$!$&'()*+;=:@{}[]%25", 0, CURLU_URLENCODE, CURLUE_OK, CURLUE_OK}, {NULL, /* start fresh! */ - "scheme=https,path=/,url=\"\",", /* incomplete url, redirect to "" */ + "scheme=https,path=/,url=\"\",", /* incomplete URL, redirect to "" */ "https://example.com/", 0, 0, CURLUE_OK, CURLUE_MALFORMED_INPUT}, {NULL, /* start fresh! */ @@ -1137,8 +1224,8 @@ static const struct setcase set_parts_list[] = { "https://host:1234/", 0, 0, CURLUE_OK, CURLUE_BAD_PORT_NUMBER}, {"https://host/", - "path=%4A%4B%4C,", - "https://host/%4a%4b%4c", + "path=%4A%4b%4C,", + "https://host/%4A%4B%4C", 0, 0, CURLUE_OK, CURLUE_OK}, {"https://host/mooo?q#f", "path=NULL,query=NULL,fragment=NULL,", @@ -1324,6 +1411,33 @@ static const struct redircase set_url_list[] = { "", /* blank redirect */ "https://example.com/", 0, 0, CURLUE_OK }, + {"file:///test?test#test", + "", "file:///test?test", + 0, 0, CURLUE_OK}, + {"https://example.com/path?query#frag", + "", "https://example.com/path?query", + 0, 0, CURLUE_OK}, + {"ftp://example.com/dir/file#anchor", + "", "ftp://example.com/dir/file", + 0, 0, CURLUE_OK}, + {"http://example.com/path#frag", + "", "http://example.com/path", + 0, 0, CURLUE_OK}, + {"http://example.com/#frag", + "", "http://example.com/", + 0, 0, CURLUE_OK}, + {"http://user:pass@example.com/path?query#frag", + "", "http://user:pass@example.com/path?query", + 0, 0, CURLUE_OK}, + {"http://example.com:8080/path?query#frag", + "", "http://example.com:8080/path?query", + 0, 0, CURLUE_OK}, + {"https://user:pass@example.com:8443/path?query#frag", + "", "https://user:pass@example.com:8443/path?query", + 0, 0, CURLUE_OK}, + {"http://[::1]/path#frag", + "", "http://[::1]/path", + 0, 0, CURLUE_OK}, {"http://firstplace.example.com/want/1314", "//somewhere.example.com/reply/1314", "http://somewhere.example.com/reply/1314", @@ -1486,8 +1600,10 @@ static int set_url(void) for(i = 0; set_url_list[i].in && !error; i++) { CURLUcode rc; CURLU *urlp = curl_url(); - if(!urlp) + if(!urlp) { + error++; break; + } rc = curl_url_set(urlp, CURLUPART_URL, set_url_list[i].in, set_url_list[i].urlflags); if(!rc) { @@ -1496,7 +1612,7 @@ static int set_url(void) if(rc) { curl_mfprintf(stderr, "%s:%d Set URL %s returned %d (%s)\n", __FILE__, __LINE__, set_url_list[i].set, - rc, curl_url_strerror(rc)); + (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1504,20 +1620,18 @@ static int set_url(void) rc = curl_url_get(urlp, CURLUPART_URL, &url, 0); if(rc) { curl_mfprintf(stderr, "%s:%d Get URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } - else { - if(checkurl(set_url_list[i].in, url, set_url_list[i].out)) { - error++; - } + else if(checkurl(set_url_list[i].in, url, set_url_list[i].out)) { + error++; } curl_free(url); } } else if(rc != set_url_list[i].ucode) { curl_mfprintf(stderr, "Set URL\nin: %s\nreturned %d (expected %d)\n", - set_url_list[i].in, rc, set_url_list[i].ucode); + set_url_list[i].in, (int)rc, (int)set_url_list[i].ucode); error++; } curl_url_cleanup(urlp); @@ -1549,15 +1663,14 @@ static int setget_parts(bool has_utf8) else rc = CURLUE_OK; if(!rc) { - char *url = NULL; CURLUcode uc = updateurl(urlp, setget_parts_list[i].set, setget_parts_list[i].setflags); if(uc != setget_parts_list[i].pcode) { curl_mfprintf(stderr, "updateurl\nin: %s\nreturned %d (expected %d)\n", - setget_parts_list[i].set, uc, - setget_parts_list[i].pcode); + setget_parts_list[i].set, + (int)uc, (int)setget_parts_list[i].pcode); error++; } if(!uc) { @@ -1567,11 +1680,10 @@ static int setget_parts(bool has_utf8) setget_parts_list[i].getflags)) error++; /* add */ } - curl_free(url); } else if(rc != CURLUE_OK) { curl_mfprintf(stderr, "Set parts\nin: %s\nreturned %d (expected %d)\n", - setget_parts_list[i].in, rc, 0); + setget_parts_list[i].in, (int)rc, 0); error++; } } @@ -1604,16 +1716,16 @@ static int set_parts(void) if(uc != set_parts_list[i].pcode) { curl_mfprintf(stderr, "updateurl\nin: %s\nreturned %d (expected %d)\n", - set_parts_list[i].set, uc, set_parts_list[i].pcode); + set_parts_list[i].set, + (int)uc, (int)set_parts_list[i].pcode); error++; } if(!uc) { /* only do this if it worked */ rc = curl_url_get(urlp, CURLUPART_URL, &url, 0); - if(rc) { curl_mfprintf(stderr, "%s:%d Get URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else if(checkurl(set_parts_list[i].in, url, set_parts_list[i].out)) { @@ -1624,7 +1736,8 @@ static int set_parts(void) } else if(rc != set_parts_list[i].ucode) { curl_mfprintf(stderr, "Set parts\nin: %s\nreturned %d (expected %d)\n", - set_parts_list[i].in, rc, set_parts_list[i].ucode); + set_parts_list[i].in, + (int)rc, (int)set_parts_list[i].ucode); error++; } curl_url_cleanup(urlp); @@ -1650,23 +1763,20 @@ static int get_url(bool has_utf8) if(!rc) { char *url = NULL; rc = curl_url_get(urlp, CURLUPART_URL, &url, get_url_list[i].getflags); - if(rc) { curl_mfprintf(stderr, "%s:%d returned %d (%s). URL: '%s'\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc), + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc), get_url_list[i].in); error++; } - else { - if(checkurl(get_url_list[i].in, url, get_url_list[i].out)) { - error++; - } + else if(checkurl(get_url_list[i].in, url, get_url_list[i].out)) { + error++; } curl_free(url); } if(rc != get_url_list[i].ucode) { curl_mfprintf(stderr, "Get URL\nin: %s\nreturned %d (expected %d)\n", - get_url_list[i].in, rc, get_url_list[i].ucode); + get_url_list[i].in, (int)rc, (int)get_url_list[i].ucode); error++; } } @@ -1693,7 +1803,8 @@ static int get_parts(bool has_utf8) get_parts_list[i].urlflags); if(rc != get_parts_list[i].ucode) { curl_mfprintf(stderr, "Get parts\nin: %s\nreturned %d (expected %d)\n", - get_parts_list[i].in, rc, get_parts_list[i].ucode); + get_parts_list[i].in, + (int)rc, (int)get_parts_list[i].ucode); error++; } else if(get_parts_list[i].ucode) { @@ -1750,7 +1861,7 @@ static int append(void) ; else if(rc != append_list[i].ucode) { curl_mfprintf(stderr, "Append\nin: %s\nreturned %d (expected %d)\n", - append_list[i].in, rc, append_list[i].ucode); + append_list[i].in, (int)rc, (int)append_list[i].ucode); error++; } else if(append_list[i].ucode) { @@ -1761,7 +1872,7 @@ static int append(void) rc = curl_url_get(urlp, CURLUPART_URL, &url, 0); if(rc) { curl_mfprintf(stderr, "%s:%d Get URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1782,12 +1893,14 @@ static int scopeid(void) int error = 0; CURLUcode rc; char *url; + if(!u) + return 1; rc = curl_url_set(u, CURLUPART_URL, "https://[fe80::20c:29ff:fe9c:409b%25eth0]/hello.html", 0); if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1795,7 +1908,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1806,7 +1919,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1814,7 +1927,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1825,7 +1938,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1833,19 +1946,18 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { curl_free(url); } - rc = curl_url_set(u, CURLUPART_HOST, - "[fe80::20c:29ff:fe9c:409b%25eth0]", 0); + rc = curl_url_set(u, CURLUPART_HOST, "[fe80::20c:29ff:fe9c:409b%25eth0]", 0); if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1853,7 +1965,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1864,7 +1976,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_HOST returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1875,7 +1987,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_ZONEID returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1886,7 +1998,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_set CURLUPART_ZONEID returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } @@ -1894,7 +2006,7 @@ static int scopeid(void) if(rc != CURLUE_OK) { curl_mfprintf(stderr, "%s:%d curl_url_get CURLUPART_URL returned %d (%s)\n", - __FILE__, __LINE__, rc, curl_url_strerror(rc)); + __FILE__, __LINE__, (int)rc, curl_url_strerror(rc)); error++; } else { @@ -1909,52 +2021,80 @@ static int scopeid(void) static int get_nothing(void) { CURLU *u = curl_url(); - if(u) { - char *p; - CURLUcode rc; - - rc = curl_url_get(u, CURLUPART_SCHEME, &p, 0); - if(rc != CURLUE_NO_SCHEME) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + int error = 0; + CURLUcode rc; + char *p = NULL; + if(!u) + return 1; - rc = curl_url_get(u, CURLUPART_HOST, &p, 0); - if(rc != CURLUE_NO_HOST) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_SCHEME, &p, 0); + if(rc != CURLUE_NO_SCHEME) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_USER, &p, 0); - if(rc != CURLUE_NO_USER) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_HOST, &p, 0); + if(rc != CURLUE_NO_HOST) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_PASSWORD, &p, 0); - if(rc != CURLUE_NO_PASSWORD) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_USER, &p, 0); + if(rc != CURLUE_NO_USER) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_OPTIONS, &p, 0); - if(rc != CURLUE_NO_OPTIONS) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_PASSWORD, &p, 0); + if(rc != CURLUE_NO_PASSWORD) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_PATH, &p, 0); - if(rc != CURLUE_OK) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); - else - curl_free(p); + rc = curl_url_get(u, CURLUPART_OPTIONS, &p, 0); + if(rc != CURLUE_NO_OPTIONS) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_QUERY, &p, 0); - if(rc != CURLUE_NO_QUERY) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_PATH, &p, 0); + if(rc != CURLUE_OK) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + } + else + curl_free(p); - rc = curl_url_get(u, CURLUPART_FRAGMENT, &p, 0); - if(rc != CURLUE_NO_FRAGMENT) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_get(u, CURLUPART_QUERY, &p, 0); + if(rc != CURLUE_NO_QUERY) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - rc = curl_url_get(u, CURLUPART_ZONEID, &p, 0); - if(rc != CURLUE_NO_ZONEID) - curl_mfprintf(stderr, "unexpected return code %d on line %d\n", rc, - __LINE__); + rc = curl_url_get(u, CURLUPART_FRAGMENT, &p, 0); + if(rc != CURLUE_NO_FRAGMENT) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + curl_free(p); + } - curl_url_cleanup(u); + rc = curl_url_get(u, CURLUPART_ZONEID, &p, 0); + if(rc != CURLUE_NO_ZONEID) { + curl_mfprintf(stderr, "unexpected return code %d on line %d\n", (int)rc, + __LINE__); + error++; + curl_free(p); } - return 0; + + curl_url_cleanup(u); + + return error; } static const struct clearurlcase clear_url_list[] = { @@ -1975,29 +2115,33 @@ static int clear_url(void) { CURLU *u = curl_url(); int i, error = 0; - if(u) { - char *p = NULL; - CURLUcode rc; + CURLUcode rc; + char *p = NULL; + if(!u) + return 1; - for(i = 0; clear_url_list[i].in && !error; i++) { - rc = curl_url_set(u, clear_url_list[i].part, clear_url_list[i].in, 0); - if(rc != CURLUE_OK) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + for(i = 0; clear_url_list[i].in && !error; i++) { + rc = curl_url_set(u, clear_url_list[i].part, clear_url_list[i].in, 0); + if(rc != CURLUE_OK) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + } - rc = curl_url_set(u, CURLUPART_URL, NULL, 0); - if(rc != CURLUE_OK) - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + rc = curl_url_set(u, CURLUPART_URL, NULL, 0); + if(rc != CURLUE_OK) { + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; + } - rc = curl_url_get(u, clear_url_list[i].part, &p, 0); - if(rc != clear_url_list[i].ucode || - (clear_url_list[i].out && strcmp(p, clear_url_list[i].out) != 0)) { + rc = curl_url_get(u, clear_url_list[i].part, &p, 0); + if(rc != clear_url_list[i].ucode || + (p && clear_url_list[i].out && strcmp(p, clear_url_list[i].out))) { - curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); - error++; - } - if(rc == CURLUE_OK) - curl_free(p); + curl_mfprintf(stderr, "unexpected return code line %d\n", __LINE__); + error++; } + if(rc == CURLUE_OK) + curl_free(p); } curl_url_cleanup(u); @@ -2055,7 +2199,7 @@ static int huge(void) if(!rc) { curl_url_get(urlp, part[i], &partp, 0); if(!partp || strcmp(partp, &bigpart[1 - (i == 4)])) { - curl_mprintf("URL %d part %u: failure\n", i, part[i]); + curl_mprintf("URL %d part %d: failure\n", i, (int)part[i]); error++; } curl_free(partp); @@ -2093,8 +2237,7 @@ static int urldup(void) goto err; for(i = 0; url[i]; i++) { - CURLUcode rc = curl_url_set(h, CURLUPART_URL, url[i], - CURLU_GUESS_SCHEME); + CURLUcode rc = curl_url_set(h, CURLUPART_URL, url[i], CURLU_GUESS_SCHEME); if(rc) goto err; copy = curl_url_dup(h); @@ -2132,7 +2275,7 @@ static int urldup(void) static int test_api_errors(void) { CURLU *u = curl_url(); - char *p; + char *p = NULL; CURLUcode rc; if(!u) return 1; diff --git a/tests/libtest/lib1565.c b/tests/libtest/lib1565.c index e6c266a7e5f4..825d88e2ed4d 100644 --- a/tests/libtest/lib1565.c +++ b/tests/libtest/lib1565.c @@ -93,7 +93,7 @@ static CURLcode test_lib1565(const char *URL) int started_num = 0; int finished_num = 0; pthread_t tid = 0; - bool tid_valid = false; + bool tid_valid = FALSE; struct CURLMsg *message; start_test_timing(); @@ -106,7 +106,7 @@ static CURLcode test_lib1565(const char *URL) rc = pthread_create(&tid, NULL, t1565_run_thread, NULL); if(!rc) - tid_valid = true; + tid_valid = TRUE; else { curl_mfprintf(stderr, "%s:%d Could not create thread, errno %d\n", __FILE__, __LINE__, rc); @@ -130,7 +130,7 @@ static CURLcode test_lib1565(const char *URL) else { curl_mfprintf(stderr, "%s:%d Got an unexpected message from curl: %d\n", - __FILE__, __LINE__, message->msg); + __FILE__, __LINE__, (int)message->msg); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } diff --git a/tests/libtest/lib1571.c b/tests/libtest/lib1571.c index adb6cc21554a..2cf218bf224f 100644 --- a/tests/libtest/lib1571.c +++ b/tests/libtest/lib1571.c @@ -40,23 +40,23 @@ static CURLcode test_lib1571(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); if((testnum == 1571) || (testnum == 1572) || (testnum == 1575) || (testnum == 1581)) { - test_setopt(curl, CURLOPT_POSTFIELDS, "moo"); + easy_setopt(curl, CURLOPT_POSTFIELDS, "moo"); } if(testnum == 1581) { - test_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); + easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); } - test_setopt(curl, CURLOPT_CUSTOMREQUEST, "IGLOO"); + easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "IGLOO"); if((testnum == 1574) || (testnum == 1575)) { - test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); } else { - test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); } result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1576.c b/tests/libtest/lib1576.c index 5357f2ec13fe..64aaa4c5ea57 100644 --- a/tests/libtest/lib1576.c +++ b/tests/libtest/lib1576.c @@ -64,20 +64,20 @@ static CURLcode test_lib1576(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1576_read_cb); - test_setopt(curl, CURLOPT_SEEKFUNCTION, t1576_seek_callback); - test_setopt(curl, CURLOPT_INFILESIZE, (long)t1576_datalen); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1576_read_cb); + easy_setopt(curl, CURLOPT_SEEKFUNCTION, t1576_seek_callback); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)t1576_datalen); - test_setopt(curl, CURLOPT_CUSTOMREQUEST, "CURL"); + easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "CURL"); if(testnum == 1578 || testnum == 1580) { - test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_FIRSTONLY); } else { - test_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE); } /* Remove "Expect: 100-continue" */ pHeaderList = curl_slist_append(pHeaderList, "Expect:"); diff --git a/tests/libtest/lib1582.c b/tests/libtest/lib1582.c index 31e8d17c41c1..ec9624be940b 100644 --- a/tests/libtest/lib1582.c +++ b/tests/libtest/lib1582.c @@ -40,13 +40,13 @@ static CURLcode test_lib1582(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_NEGOTIATE); - test_setopt(curl, CURLOPT_USERPWD, ":"); - test_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - test_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_NEGOTIATE); + easy_setopt(curl, CURLOPT_USERPWD, ":"); + easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1587.c b/tests/libtest/lib1587.c index ad66c003f0a1..5789804acb47 100644 --- a/tests/libtest/lib1587.c +++ b/tests/libtest/lib1587.c @@ -21,18 +21,13 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "first.h" #ifdef USE_OPENSSL - -#include -#include -#include #include #ifdef HAVE_BORINGSSL_LIKE -/* BoringSSL and AWS-LC */ +/* AWS-LC and BoringSSL */ typedef uint32_t opt1587; #else typedef uint64_t opt1587; diff --git a/tests/libtest/lib1588.c b/tests/libtest/lib1588.c index fd5cd4717095..896a08a8544c 100644 --- a/tests/libtest/lib1588.c +++ b/tests/libtest/lib1588.c @@ -102,7 +102,7 @@ static CURLcode test_lib1588(const char *URL) curl_msnprintf(proxy2_resolve, sizeof(proxy2_resolve), "secondproxy:%s:%s", libtest_arg3, libtest_arg2); - /* we connect to the fake host name but the right port number */ + /* we connect to the fake hostname but the right port number */ curl_msnprintf(proxy1_connect, sizeof(proxy1_connect), "firstproxy:%s", libtest_arg3); curl_msnprintf(proxy2_connect, sizeof(proxy2_connect), diff --git a/tests/libtest/lib1589.c b/tests/libtest/lib1589.c new file mode 100644 index 000000000000..e8d029034aa4 --- /dev/null +++ b/tests/libtest/lib1589.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 + * + ***************************************************************************/ +/* + * argv1 = URL + * argv2 = proxy host + * argv3 = proxy1 port + * argv4 = proxy2 port + * argv5 = proxyuser:password + */ + +#include "first.h" + +static CURLcode init1589(CURL *curl, const char *url, + const char *userpwd, const char *proxy, + int port) +{ + CURLcode result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY, proxy); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYPORT, (long)port); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; +#if 0 + res_easy_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + if(result) + goto init_failed; +#endif + + res_easy_setopt(curl, CURLOPT_HEADER, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1589(CURL *curl, const char *url, const char *userpwd, + const char *proxy, int port) +{ + CURLcode result = CURLE_OK; + + result = init1589(curl, url, userpwd, proxy, port); + if(result) + return result; + + return curl_easy_perform(curl); +} + +static CURLcode test_lib1589(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + const char *proxy = libtest_arg2; + /* !checksrc! disable BANNEDFUNC 2 */ + int port1 = atoi(libtest_arg3); + int port2 = atoi(libtest_arg4); + const char *proxyuserpwd = libtest_arg5; + + if(test_argc < 5) + return TEST_ERR_MAJOR_BAD; + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + start_test_timing(); + + result = run1589(curl, URL, proxyuserpwd, proxy, port1); + if(result) + goto test_cleanup; + + curl_mfprintf(stderr, "lib1589: now we do the request again\n"); + + result = run1589(curl, URL, proxyuserpwd, proxy, port2); + +test_cleanup: + + /* proper cleanup sequence - type PB */ + + curl_easy_cleanup(curl); + curl_global_cleanup(); + return result; +} diff --git a/tests/libtest/lib1591.c b/tests/libtest/lib1591.c index 5b2b913037b8..97b234234fa4 100644 --- a/tests/libtest/lib1591.c +++ b/tests/libtest/lib1591.c @@ -23,7 +23,7 @@ ***************************************************************************/ /* - * This unit test PUT http data over proxy. Proxy header will be different + * This unit test PUT http data over proxy. Proxy header is different * from server http header */ @@ -34,15 +34,16 @@ static size_t consumed = 0; static size_t t1591_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { static const char testdata[] = "Hello Cloud!\r\n"; + static size_t const datalen = sizeof(testdata) - 1; size_t amount = nmemb * size; /* Total bytes curl wants */ - if(consumed == strlen(testdata)) { + if(consumed == datalen) { return 0; } - if(amount > strlen(testdata) - consumed) { - amount = strlen(testdata); + if(amount > datalen - consumed) { + amount = datalen - consumed; } consumed += amount; @@ -97,12 +98,12 @@ static CURLcode test_lib1591(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1591_read_cb); - test_setopt(curl, CURLOPT_TRAILERFUNCTION, t1591_trailers_callback); - test_setopt(curl, CURLOPT_TRAILERDATA, NULL); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1591_read_cb); + easy_setopt(curl, CURLOPT_TRAILERFUNCTION, t1591_trailers_callback); + easy_setopt(curl, CURLOPT_TRAILERDATA, NULL); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1592.c b/tests/libtest/lib1592.c index 29dbe0b70033..497659b10062 100644 --- a/tests/libtest/lib1592.c +++ b/tests/libtest/lib1592.c @@ -29,12 +29,11 @@ * only tests whichever resolver curl is actually built with. */ -/* We are willing to wait a generous two seconds for the removal. This is - as low as we can go while still easily supporting SIGALRM timing for the - non-threaded blocking resolver. It does not matter that much because when - the test passes, we never wait this long. We set it much higher via - the default TEST_HANG_TIMEOUT to avoid issues when running on overloaded - CI machines. */ +/* We are waiting a generous two seconds for the removal. This is as low as we + can go while still easily supporting SIGALRM timing for the non-threaded + blocking resolver. It does not matter that much because when the test + passes, we never wait this long. We set it much higher via the default + TEST_HANG_TIMEOUT to avoid issues when running on overloaded CI machines. */ #include "first.h" @@ -56,34 +55,33 @@ static CURLcode test_lib1592(const char *URL) easy_setopt(curl, CURLOPT_VERBOSE, 1L); easy_setopt(curl, CURLOPT_URL, URL); - /* Set a DNS server that hopefully will not respond when using c-ares. */ + /* Set a DNS server that hopefully does not respond when using c-ares. */ if(curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, "0.0.0.0") == CURLE_OK) /* Since we could set the DNS server, presume we are working with a resolver that can be cancelled (i.e. c-ares). Thus, curl_multi_remove_handle() should not block even when the resolver - request is outstanding. So, set a request timeout _longer_ than the - test hang timeout so we will fail if the handle removal call incorrectly + request is outstanding. Thus, set a request timeout _longer_ than the + test hang timeout so we fail if the handle removal call incorrectly blocks. */ timeout = TEST_HANG_TIMEOUT * 2; else { /* If we cannot set the DNS server, presume that we are configured to use a resolver that cannot be cancelled (i.e. the threaded resolver or the - non-threaded blocking resolver). So, we just test that the + non-threaded blocking resolver). Thus, we test that the curl_multi_remove_handle() call does finish well within our test timeout. - But, it is unlikely that the resolver request will take any time at + But, it is unlikely that the resolver request takes any time at all because we have not been able to configure the resolver to use an - non-responsive DNS server. At least we exercise the flow. - */ + non-responsive DNS server. At least we exercise the flow. */ curl_mfprintf(stderr, "CURLOPT_DNS_SERVERS not supported; " - "assuming curl_multi_remove_handle() will block\n"); + "assuming curl_multi_remove_handle() does block\n"); timeout = TEST_HANG_TIMEOUT / 2; } /* Setting a timeout on the request should ensure that even if we have to - wait for the resolver during curl_multi_remove_handle(), it will not take + wait for the resolver during curl_multi_remove_handle(), it does not take longer than this, because the resolver request inherits its timeout from this. */ easy_setopt(curl, CURLOPT_TIMEOUT_MS, timeout); diff --git a/tests/libtest/lib1597.c b/tests/libtest/lib1597.c index 5e3fab8cade0..11da472c4d8f 100644 --- a/tests/libtest/lib1597.c +++ b/tests/libtest/lib1597.c @@ -96,7 +96,8 @@ static CURLcode test_lib1597(const char *URL) for(i = 0; prots[i].in; i++) { result = curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, prots[i].in); if(result != *prots[i].result_exp) { - curl_mprintf("unexpectedly '%s' returned %d\n", prots[i].in, result); + curl_mprintf("unexpectedly '%s' returned %d\n", prots[i].in, + (int)result); break; } } diff --git a/tests/libtest/lib1598.c b/tests/libtest/lib1598.c index 50d715ffe772..961c424e680a 100644 --- a/tests/libtest/lib1598.c +++ b/tests/libtest/lib1598.c @@ -23,7 +23,7 @@ ***************************************************************************/ /* - * This unit test PUT http data over proxy. Proxy header will be different + * This unit test PUT http data over proxy. Proxy header is different * from server http header */ @@ -82,13 +82,13 @@ static CURLcode test_lib1598(const char *URL) hhl = list; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPHEADER, hhl); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(post_data)); - test_setopt(curl, CURLOPT_POSTFIELDS, post_data); - test_setopt(curl, CURLOPT_TRAILERFUNCTION, t1598_trailers_callback); - test_setopt(curl, CURLOPT_TRAILERDATA, NULL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPHEADER, hhl); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(post_data)); + easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); + easy_setopt(curl, CURLOPT_TRAILERFUNCTION, t1598_trailers_callback); + easy_setopt(curl, CURLOPT_TRAILERDATA, NULL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1647.c b/tests/libtest/lib1647.c new file mode 100644 index 000000000000..8060e1bfe954 --- /dev/null +++ b/tests/libtest/lib1647.c @@ -0,0 +1,120 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 + * + ***************************************************************************/ +/* + * argv1 = the first URL + * argv2 = URL2 + * argv3 = credentials 1 + * argv4 = credentials 2 + */ + +#include "first.h" + +/* this is meant to pick up the proxy from the environment variable */ +static CURLcode init1647(CURL *curl, const char *url, const char *userpwd) +{ + CURLcode result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYUSERPWD, userpwd); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYPEER, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY_SSL_VERIFYHOST, 0L); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1647(CURL *curl, const char *url, const char *userpwd) +{ + CURLcode result = CURLE_OK; + + result = init1647(curl, url, userpwd); + if(result) + return result; + + return curl_easy_perform(curl); +} + +static CURLcode test_lib1647(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + start_test_timing(); + + curl_mprintf("--- First get '%s'\n", URL); + result = run1647(curl, URL, libtest_arg3); + if(result) + goto test_cleanup; + + curl_mprintf("--- Then get '%s'\n", libtest_arg2); + result = run1647(curl, libtest_arg2, libtest_arg4); + +test_cleanup: + + /* proper cleanup sequence - type PB */ + + curl_easy_cleanup(curl); + curl_global_cleanup(); + return result; +} diff --git a/tests/libtest/lib1648.c b/tests/libtest/lib1648.c new file mode 100644 index 000000000000..91afbcead4bc --- /dev/null +++ b/tests/libtest/lib1648.c @@ -0,0 +1,135 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 + * + ***************************************************************************/ +/* + * URL = host + * arg2 = port + */ + +#include "first.h" + +/* this is meant to pick up the proxy from the environment variable */ +static CURLcode init1648(CURL *curl, const char *url, const char *proxy) +{ + CURLcode result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_PROXY, proxy); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1648(CURL *curl, const char *url, const char *proxy) +{ + CURLcode result = CURLE_OK; + + result = init1648(curl, url, proxy); + if(result) + return result; + + return curl_easy_perform(curl); +} + +#define GET_THIS "http://example.com/" + +/* + * First get the URL over 'firstproxy' with auth. + * Then clear the auth and get the URL again over 'secondproxy'. + */ +static CURLcode test_lib1648(const char *hostip) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + struct curl_slist *host = NULL; + struct curl_slist *host2 = NULL; + char proxy1_resolve[128]; + char proxy2_resolve[128]; + char proxy1_connect[128]; + char proxy2_connect[128]; + + curl_msnprintf(proxy1_resolve, sizeof(proxy1_resolve), + "firstproxy:%s:%s", libtest_arg2, hostip); + curl_msnprintf(proxy2_resolve, sizeof(proxy2_resolve), + "secondproxy:%s:%s", libtest_arg2, hostip); + + /* we connect to the fake hostname but the right port number */ + curl_msnprintf(proxy1_connect, sizeof(proxy1_connect), + "firstproxy:%s", libtest_arg2); + curl_msnprintf(proxy2_connect, sizeof(proxy2_connect), + "secondproxy:%s", libtest_arg2); + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + host = curl_slist_append(NULL, proxy1_resolve); + if(!host) + goto test_cleanup; + host2 = curl_slist_append(host, proxy2_resolve); + if(!host2) + goto test_cleanup; + host = host2; + + start_test_timing(); + + easy_setopt(curl, CURLOPT_RESOLVE, host); + easy_setopt(curl, CURLOPT_PROXYUSERPWD, "victim:secret"); + + curl_mprintf("--- First get over %s\n", proxy1_connect); + result = run1648(curl, GET_THIS, proxy1_connect); + if(result) + goto test_cleanup; + + easy_setopt(curl, CURLOPT_PROXYUSERPWD, NULL); + + curl_mprintf("--- Then over '%s'\n", proxy2_connect); + result = run1648(curl, GET_THIS, proxy2_connect); + +test_cleanup: + + /* proper cleanup sequence - type PB */ + + curl_easy_cleanup(curl); + curl_global_cleanup(); + curl_slist_free_all(host); + return result; +} diff --git a/tests/libtest/lib1649.c b/tests/libtest/lib1649.c new file mode 100644 index 000000000000..2dd66c0231a2 --- /dev/null +++ b/tests/libtest/lib1649.c @@ -0,0 +1,90 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "first.h" + +/* this is meant to pick up the proxy from the environment variable */ +static CURLcode init1649(CURL *curl, const char *url) +{ + CURLcode result = CURLE_OK; + + res_easy_setopt(curl, CURLOPT_URL, url); + if(result) + goto init_failed; + + res_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if(result) + goto init_failed; + + return CURLE_OK; /* success */ + +init_failed: + return result; /* failure */ +} + +static CURLcode run1649(CURL *curl, const char *url) +{ + CURLcode result = CURLE_OK; + + result = init1649(curl, url); + if(result) + return result; + + return curl_easy_perform(curl); +} + +static CURLcode test_lib1649(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + + res_global_init(CURL_GLOBAL_ALL); + if(result) + return result; + + curl = curl_easy_init(); + if(!curl) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + start_test_timing(); + + easy_setopt(curl, CURLOPT_REFERER, "https://secret.example.com/"); + + result = run1649(curl, URL); + if(result) + goto test_cleanup; + + /* reset it */ + easy_setopt(curl, CURLOPT_REFERER, NULL); + + result = run1649(curl, URL); + +test_cleanup: + curl_easy_cleanup(curl); + curl_global_cleanup(); + return result; +} diff --git a/tests/libtest/lib1686.c b/tests/libtest/lib1686.c new file mode 100644 index 000000000000..b7817463d25b --- /dev/null +++ b/tests/libtest/lib1686.c @@ -0,0 +1,93 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "first.h" + +#define FIRSTHOST "first.test" +#define SECONDHOST "second.test" + +static CURLcode test_lib1686(const char *hostip) +{ + CURL *curl = NULL; + CURLcode result = CURLE_OK; + const char *httpport = libtest_arg2; + char firsturl[100]; + char secondurl[100]; + char firstres[100]; + char secondres[100]; + struct curl_slist *host = NULL; + struct curl_slist *host2 = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + /* create strings for CURLOPT_RESOLVE */ + curl_msnprintf(firstres, sizeof(firstres), "%s:%s:%s", + FIRSTHOST, httpport, hostip); + curl_msnprintf(secondres, sizeof(secondres), "%s:%s:%s", + SECONDHOST, httpport, hostip); + + /* create URLs */ + curl_msnprintf(firsturl, sizeof(firsturl), "http://%s:%s/api", + FIRSTHOST, httpport); + curl_msnprintf(secondurl, sizeof(secondurl), "http://%s:%s/hook", + SECONDHOST, httpport); + + host = curl_slist_append(NULL, firstres); + if(!host) + goto test_cleanup; + host2 = curl_slist_append(host, secondres); + if(!host2) + goto test_cleanup; + host = host2; + + curl = curl_easy_init(); + if(curl) { + easy_setopt(curl, CURLOPT_RESOLVE, host); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + easy_setopt(curl, CURLOPT_USERPWD, "alice:bond"); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + + easy_setopt(curl, CURLOPT_URL, firsturl); + result = curl_easy_perform(curl); + if(result) + goto test_cleanup; + + easy_setopt(curl, CURLOPT_URL, secondurl); + result = curl_easy_perform(curl); + if(result) + goto test_cleanup; + + easy_setopt(curl, CURLOPT_USERPWD, "bob:secret"); + easy_setopt(curl, CURLOPT_URL, secondurl); + result = curl_easy_perform(curl); + } + +test_cleanup: + curl_easy_cleanup(curl); + curl_global_cleanup(); + curl_slist_free_all(host); + return result; +} diff --git a/tests/libtest/lib1901.c b/tests/libtest/lib1901.c index 80d06080de0f..a6ccbdb0cb00 100644 --- a/tests/libtest/lib1901.c +++ b/tests/libtest/lib1901.c @@ -25,13 +25,7 @@ static size_t t1901_read_cb(char *ptr, size_t size, size_t nmemb, void *stream) { - static const char *chunks[] = { - "one", - "two", - "three", - "four", - NULL - }; + static const char *chunks[] = { "one", "two", "three", "four", NULL }; static int ix = 0; (void)stream; if(chunks[ix]) { diff --git a/tests/libtest/lib1902.c b/tests/libtest/lib1902.c index da3b23a349d4..b2ceb3dc39b5 100644 --- a/tests/libtest/lib1902.c +++ b/tests/libtest/lib1902.c @@ -33,7 +33,7 @@ static CURLcode test_lib1902(const char *URL) curl = curl_easy_init(); if(curl) { easy_setopt(curl, CURLOPT_COOKIEFILE, URL); - easy_setopt(curl, CURLOPT_COOKIEJAR, URL); + easy_setopt(curl, CURLOPT_COOKIEJAR, URL); /* Do not perform any actual network operation, the issue occur when not calling curl.*perform */ diff --git a/tests/libtest/lib1906.c b/tests/libtest/lib1906.c index 5ac2105dfe45..e649263338af 100644 --- a/tests/libtest/lib1906.c +++ b/tests/libtest/lib1906.c @@ -45,12 +45,11 @@ static CURLcode test_lib1906(const char *URL) if(result != CURLE_COULDNT_CONNECT && result != CURLE_OPERATION_TIMEDOUT) { curl_mfprintf(stderr, "failure expected, " "curl_easy_perform returned %d: <%s>, <%s>\n", - result, curl_easy_strerror(result), error_buffer); + (int)result, curl_easy_strerror(result), error_buffer); if(result == CURLE_OK) result = TEST_ERR_MAJOR_BAD; /* force an error return */ goto test_cleanup; } - result = CURLE_OK; /* reset for next use */ /* print the used URL */ curl_url_get(curlu, CURLUPART_URL, &url_after, 0); @@ -65,7 +64,7 @@ static CURLcode test_lib1906(const char *URL) if(result) curl_mfprintf(stderr, "success expected, " "curl_easy_perform returned %d: <%s>, <%s>\n", - result, curl_easy_strerror(result), error_buffer); + (int)result, curl_easy_strerror(result), error_buffer); /* print URL */ curl_url_get(curlu, CURLUPART_URL, &url_after, 0); diff --git a/tests/libtest/lib1907.c b/tests/libtest/lib1907.c index 6aac37515225..3b91d846f739 100644 --- a/tests/libtest/lib1907.c +++ b/tests/libtest/lib1907.c @@ -39,7 +39,7 @@ static CURLcode test_lib1907(const char *URL) if(!result) curl_mfprintf(stderr, "failure expected, " "curl_easy_perform returned %d: <%s>, <%s>\n", - result, curl_easy_strerror(result), error_buffer); + (int)result, curl_easy_strerror(result), error_buffer); /* print the used URL */ if(!curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url_after)) diff --git a/tests/libtest/lib1908.c b/tests/libtest/lib1908.c index f7ead3406eb7..9d6a7a12199a 100644 --- a/tests/libtest/lib1908.c +++ b/tests/libtest/lib1908.c @@ -52,7 +52,7 @@ static CURLcode test_lib1908(const char *URL) curl_easy_reset(curl); /* using the same filename for the alt-svc cache, this clobbers the - content just written from the 'curldupe' handle */ + content written from the 'curldupe' handle */ curl_easy_cleanup(curl); } curl_global_cleanup(); diff --git a/tests/libtest/lib1911.c b/tests/libtest/lib1911.c index f265174891d7..662093a61fec 100644 --- a/tests/libtest/lib1911.c +++ b/tests/libtest/lib1911.c @@ -43,7 +43,7 @@ static CURLcode test_lib1911(const char *URL) return TEST_ERR_EASY_INIT; } - /* make it a null-terminated C string with just As */ + /* make it a null-terminated C string with only As */ memset(testbuf, 'A', MAX_INPUT_LENGTH + 1); testbuf[MAX_INPUT_LENGTH + 1] = 0; @@ -78,7 +78,7 @@ static CURLcode test_lib1911(const char *URL) default: /* all other return codes are unexpected */ curl_mfprintf(stderr, "curl_easy_setopt(%s...) returned %d\n", - o->name, result); + o->name, (int)result); error++; break; } diff --git a/tests/libtest/lib1912.c b/tests/libtest/lib1912.c index 98623bd339f5..65de8b3ffb95 100644 --- a/tests/libtest/lib1912.c +++ b/tests/libtest/lib1912.c @@ -23,16 +23,17 @@ ***************************************************************************/ #include "first.h" -#define print_err(name, exp) \ - curl_mfprintf(stderr, "Type mismatch for CURLOPT_%s (expected %s)\n", \ - name, exp) - static CURLcode test_lib1912(const char *URL) { /* Only test if GCC/clang type checking is available */ int error = 0; #ifdef CURLINC_TYPECHECK_GCC_H const struct curl_easyoption *o; + +#define print_err(name, exp) \ + curl_mfprintf(stderr, "Type mismatch for CURLOPT_%s (expected %s)\n", \ + name, exp) + for(o = curl_easy_option_next(NULL); o; o = curl_easy_option_next(o)) { /* Test for mismatch OR missing typecheck macros */ if(curlcheck_long_option(o->id) != diff --git a/tests/libtest/lib1915.c b/tests/libtest/lib1915.c index d5dd4dc2fcdf..eebf7ba755b1 100644 --- a/tests/libtest/lib1915.c +++ b/tests/libtest/lib1915.c @@ -38,7 +38,7 @@ static CURLSTScode hstsread(CURL *curl, struct curl_hstsentry *e, void *userp) }; static const struct entry preload_hosts[] = { -#if (SIZEOF_TIME_T < 5) +#if SIZEOF_TIME_T < 5 { "1.example.com", "20370320 01:02:03" }, { "2.example.com.", "20370320 03:02:01" }, { "3.example.com", "20370319 01:02:03" }, @@ -124,7 +124,7 @@ static CURLcode test_lib1915(const char *URL) curl = NULL; if(result == CURLE_OPERATION_TIMEDOUT) /* we expect that on Windows */ result = CURLE_COULDNT_CONNECT; - curl_mprintf("First request returned %d\n", result); + curl_mprintf("First request returned %d\n", (int)result); result = CURLE_OK; easy_init(curl); @@ -141,7 +141,7 @@ static CURLcode test_lib1915(const char *URL) result = curl_easy_perform(curl); curl_easy_cleanup(curl); curl = NULL; - curl_mprintf("Second request returned %d\n", result); + curl_mprintf("Second request returned %d\n", (int)result); test_cleanup: curl_easy_cleanup(curl); diff --git a/tests/libtest/lib1916.c b/tests/libtest/lib1916.c index a67b68fc9cd5..575080d09ff0 100644 --- a/tests/libtest/lib1916.c +++ b/tests/libtest/lib1916.c @@ -45,7 +45,7 @@ static CURLcode test_lib1916(const char *URL) } result = curl_easy_perform(curl); if(result) { - curl_mprintf("result: %d\n", result); + curl_mprintf("result: %d\n", (int)result); } curl_easy_cleanup(curl); } diff --git a/tests/libtest/lib1918.c b/tests/libtest/lib1918.c index 1c8ccb78f984..4932c1cfa6bb 100644 --- a/tests/libtest/lib1918.c +++ b/tests/libtest/lib1918.c @@ -38,11 +38,11 @@ static CURLcode test_lib1918(const char *URL) if(ename->id != o->id) { curl_mprintf("name lookup id %d does not match %d\n", - ename->id, o->id); + (int)ename->id, (int)o->id); } else if(eid->id != o->id) { curl_mprintf("ID lookup %d does not match %d\n", - ename->id, o->id); + (int)ename->id, (int)o->id); } } curl_global_cleanup(); diff --git a/tests/libtest/lib1920.c b/tests/libtest/lib1920.c index fe7e8b3206e3..84a747ee6f2c 100644 --- a/tests/libtest/lib1920.c +++ b/tests/libtest/lib1920.c @@ -33,9 +33,9 @@ static CURLcode test_lib1920(const char *URL) curl = curl_easy_init(); if(curl) { easy_setopt(curl, CURLOPT_COOKIEFILE, libtest_arg2); - easy_setopt(curl, CURLOPT_COOKIEJAR, libtest_arg2); - easy_setopt(curl, CURLOPT_URL, URL); - easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_COOKIEJAR, libtest_arg2); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); @@ -44,7 +44,7 @@ static CURLcode test_lib1920(const char *URL) curl_easy_reset(curl); /* set the cookie jar name so that curl knows where to store the cookies after reset */ - easy_setopt(curl, CURLOPT_COOKIEJAR, libtest_arg2); + easy_setopt(curl, CURLOPT_COOKIEJAR, libtest_arg2); } } diff --git a/tests/libtest/lib1921.c b/tests/libtest/lib1921.c new file mode 100644 index 000000000000..8799c2d96092 --- /dev/null +++ b/tests/libtest/lib1921.c @@ -0,0 +1,52 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "first.h" + +static CURLcode test_lib1921(const char *URL) +{ + CURLU *u = curl_url(); + CURLUcode rc; + if(!u) + return CURLE_FAILED_INIT; + (void)URL; /* unused */ + /* u->scheme remains NULL */ + rc = curl_url_set(u, CURLUPART_HOST, "example.com", 0); + if(!rc) + rc = curl_url_set(u, CURLUPART_PATH, "/original", 0); + + if(!rc) + /* Relative URL + CURLU_DEFAULT_SCHEME reaches redirect_url() */ + rc = curl_url_set(u, CURLUPART_URL, "/newpath", CURLU_DEFAULT_SCHEME); + + if(!rc) { + char *url; + rc = curl_url_get(u, CURLUPART_URL, &url, 0); + if(!rc) { + curl_mprintf("URL: %s\n", url); + curl_free(url); + } + } + curl_url_cleanup(u); + return rc ? CURLE_BAD_FUNCTION_ARGUMENT : CURLE_OK; +} diff --git a/tests/libtest/lib1922.c b/tests/libtest/lib1922.c new file mode 100644 index 000000000000..17b9cb8e34f3 --- /dev/null +++ b/tests/libtest/lib1922.c @@ -0,0 +1,116 @@ +/*************************************************************************** + * _ _ ____ _ + * 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 "first.h" + +static CURLcode test_lib1922(const char *URL) +{ + CURLcode result = CURLE_OK; + CURL *curl = NULL; + CURL *dup = NULL; + struct curl_slist *resolve = NULL; + char resolve_entry[256]; + char direct_url[256]; + char http_url[256]; + char proxy_url[256]; + const char *effective = NULL; + const char *host = libtest_arg2; /* %HOSTIP */ + const char *httpport = libtest_arg3; /* %HTTPPORT */ + const char *proxyport = libtest_arg4;/* %PROXYPORT */ + + (void)URL; + + if(!host || !httpport || !proxyport) { + curl_mfprintf(stderr, + "Usage: lib1922 - \n"); + return TEST_ERR_MAJOR_BAD; + } + + /* Synthetic DNS so hsts.example.com resolves to the test server. */ + curl_msnprintf(resolve_entry, sizeof(resolve_entry), + "hsts.example.com:%s:%s", httpport, host); + resolve = curl_slist_append(NULL, resolve_entry); + if(!resolve) { + return CURLE_OUT_OF_MEMORY; + } + + curl_msnprintf(direct_url, sizeof(direct_url), + "http://hsts.example.com:%s/%d", httpport, 1922); + curl_msnprintf(http_url, sizeof(http_url), + "http://hsts.example.com/%d", 1922); + curl_msnprintf(proxy_url, sizeof(proxy_url), + "http://%s:%s", host, proxyport); + + global_init(CURL_GLOBAL_ALL); + easy_init(curl); + + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_RESOLVE, resolve); + easy_setopt(curl, CURLOPT_URL, direct_url); + easy_setopt(curl, CURLOPT_HSTS_CTRL, CURLHSTS_ENABLE); + + /* Direct HTTP request: Server returns Strict-Transport-Security. + * CURL_HSTS_HTTP env var (set in the test) allows processing it over + * HTTP in debug builds, populating the live HSTS cache. */ + result = curl_easy_perform(curl); + if(result) { + curl_mfprintf(stderr, "First perform failed: %d (%s)\n", + (int)result, curl_easy_strerror(result)); + goto test_cleanup; + } + curl_mprintf("First request: HSTS cache populated\n"); + + dup = curl_easy_duphandle(curl); + if(!dup) { + result = CURLE_FAILED_INIT; + goto test_cleanup; + } + + /* Point the dup at the plain HTTP URL for the same hostname, via a proxy. + * The copied HSTS cache upgrades the URL to HTTPS, causing a CONNECT to + * port 443. The test proxy rejects CONNECT with 403, so curl returns + * CURLE_COULDNT_CONNECT (7). The CONNECT to port 443 is itself the proof + * of the upgrade. */ + easy_setopt(dup, CURLOPT_URL, http_url); + easy_setopt(dup, CURLOPT_PROXY, proxy_url); + + result = curl_easy_perform(dup); + if(result != CURLE_COULDNT_CONNECT) { + curl_mfprintf(stderr, "Dup perform unexpected result: %d (%s)\n", + (int)result, curl_easy_strerror(result)); + goto test_cleanup; + } + + /* Confirm the dup's URL was upgraded to HTTPS by the copied HSTS cache. */ + curl_easy_getinfo(dup, CURLINFO_EFFECTIVE_URL, &effective); + if(effective) { + curl_mprintf("Dup effective URL: %s\n", effective); + } + +test_cleanup: + curl_easy_cleanup(curl); + curl_easy_cleanup(dup); + curl_slist_free_all(resolve); + curl_global_cleanup(); + return result; +} diff --git a/tests/libtest/lib1933.c b/tests/libtest/lib1933.c index bd31f9418a3e..abe4361bea09 100644 --- a/tests/libtest/lib1933.c +++ b/tests/libtest/lib1933.c @@ -42,16 +42,16 @@ static CURLcode test_lib1933(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1934.c b/tests/libtest/lib1934.c index 41959ad60ef7..1ddb8bee0b74 100644 --- a/tests/libtest/lib1934.c +++ b/tests/libtest/lib1934.c @@ -42,17 +42,17 @@ static CURLcode test_lib1934(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy"); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1935.c b/tests/libtest/lib1935.c index 9b7214405b4b..26d17c685114 100644 --- a/tests/libtest/lib1935.c +++ b/tests/libtest/lib1935.c @@ -42,17 +42,17 @@ static CURLcode test_lib1935(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy:rrr"); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy:rrr"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1936.c b/tests/libtest/lib1936.c index b4908f09735c..dba9f1ccbac4 100644 --- a/tests/libtest/lib1936.c +++ b/tests/libtest/lib1936.c @@ -42,17 +42,17 @@ static CURLcode test_lib1936(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy:rrr:sss"); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx:yyy:rrr:sss"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1937.c b/tests/libtest/lib1937.c index 03160f4ccb8c..a2493751791a 100644 --- a/tests/libtest/lib1937.c +++ b/tests/libtest/lib1937.c @@ -42,19 +42,19 @@ static CURLcode test_lib1937(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "provider1:provider2:region:service"); - test_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "provider1:provider2:region:service"); + easy_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); - test_setopt(curl, CURLOPT_POSTFIELDS, "postData"); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_POSTFIELDS, "postData"); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1938.c b/tests/libtest/lib1938.c index 6b56d7c2f820..300a092fd5a6 100644 --- a/tests/libtest/lib1938.c +++ b/tests/libtest/lib1938.c @@ -43,20 +43,20 @@ static CURLcode test_lib1938(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "provider1:provider2:region:service"); - test_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "provider1:provider2:region:service"); + easy_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); list = curl_slist_append(list, "Content-Type: application/json"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); - test_setopt(curl, CURLOPT_POSTFIELDS, data); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(data)); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_POSTFIELDS, data); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(data)); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1939.c b/tests/libtest/lib1939.c index 502b60051717..5445251df338 100644 --- a/tests/libtest/lib1939.c +++ b/tests/libtest/lib1939.c @@ -41,7 +41,7 @@ static CURLcode test_lib1939(const char *URL) /* Crash only happens when using HTTPS */ result = curl_easy_setopt(curl, CURLOPT_URL, URL); if(!result) - /* Any old HTTP tunneling proxy will do here */ + /* Any old HTTP tunneling proxy does the job here */ result = curl_easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); if(!result) { @@ -51,7 +51,7 @@ static CURLcode test_lib1939(const char *URL) mresult = curl_multi_add_handle(multi, curl); if(!mresult) - /* Run the multi handle once, just enough to start establishing an + /* Run the multi handle once, enough to start establishing an HTTPS connection. */ mresult = curl_multi_perform(multi, &running_handles); diff --git a/tests/libtest/lib1940.c b/tests/libtest/lib1940.c index a9114216a181..349d8131c67c 100644 --- a/tests/libtest/lib1940.c +++ b/tests/libtest/lib1940.c @@ -23,14 +23,6 @@ ***************************************************************************/ #include "first.h" -static size_t t1940_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; -} - static void t1940_showem(CURL *curl, int header_request, unsigned int type) { static const char *testdata[] = { @@ -95,7 +87,7 @@ static CURLcode test_lib1940(const char *URL) easy_setopt(curl, CURLOPT_VERBOSE, 1L); easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* ignores any content */ - easy_setopt(curl, CURLOPT_WRITEFUNCTION, t1940_write_cb); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); /* if there is a proxy set, use it */ if(libtest_arg2 && *libtest_arg2) { diff --git a/tests/libtest/lib1945.c b/tests/libtest/lib1945.c index 0eb5a6873e77..e76a5dc528a8 100644 --- a/tests/libtest/lib1945.c +++ b/tests/libtest/lib1945.c @@ -36,14 +36,6 @@ static void t1945_showem(CURL *curl, unsigned int type) } } -static size_t t1945_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; -} - static CURLcode test_lib1945(const char *URL) { CURL *curl; @@ -56,7 +48,7 @@ static CURLcode test_lib1945(const char *URL) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* ignores any content */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, t1945_write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); /* if there is a proxy set, use it */ if(libtest_arg2 && *libtest_arg2) { @@ -65,7 +57,7 @@ static CURLcode test_lib1945(const char *URL) } result = curl_easy_perform(curl); if(result) { - curl_mprintf("badness: %d\n", result); + curl_mprintf("badness: %d\n", (int)result); } t1945_showem(curl, CURLH_CONNECT | CURLH_HEADER | CURLH_TRAILER | CURLH_1XX); diff --git a/tests/libtest/lib1947.c b/tests/libtest/lib1947.c index 88a6336dbe82..a4abb497caa6 100644 --- a/tests/libtest/lib1947.c +++ b/tests/libtest/lib1947.c @@ -23,14 +23,6 @@ ***************************************************************************/ #include "first.h" -static size_t t1947_write_cb(char *data, size_t n, size_t l, void *userp) -{ - /* ignore the data */ - (void)data; - (void)userp; - return n * l; -} - static CURLcode test_lib1947(const char *URL) { CURL *curl; @@ -45,7 +37,7 @@ static CURLcode test_lib1947(const char *URL) /* perform a request that involves redirection */ easy_setopt(curl, CURLOPT_URL, URL); - easy_setopt(curl, CURLOPT_WRITEFUNCTION, t1947_write_cb); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); result = curl_easy_perform(curl); if(result) { diff --git a/tests/libtest/lib1948.c b/tests/libtest/lib1948.c index 947469549ed8..9b8217be8ff0 100644 --- a/tests/libtest/lib1948.c +++ b/tests/libtest/lib1948.c @@ -43,7 +43,7 @@ static CURLcode test_lib1948(const char *URL) { CURL *curl; CURLcode result = CURLE_OK; - static const char *testput = "This is test PUT data\n"; + static const char testput[] = "This is test PUT data\n"; struct put_buffer pbuf; curl_global_init(CURL_GLOBAL_DEFAULT); @@ -55,9 +55,9 @@ static CURLcode test_lib1948(const char *URL) easy_setopt(curl, CURLOPT_HEADER, 1L); easy_setopt(curl, CURLOPT_READFUNCTION, put_callback); pbuf.buf = testput; - pbuf.len = strlen(testput); + pbuf.len = sizeof(testput) - 1; easy_setopt(curl, CURLOPT_READDATA, &pbuf); - easy_setopt(curl, CURLOPT_INFILESIZE, (long)strlen(testput)); + easy_setopt(curl, CURLOPT_INFILESIZE, (long)(sizeof(testput) - 1)); easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); if(result) @@ -66,7 +66,7 @@ static CURLcode test_lib1948(const char *URL) /* POST */ easy_setopt(curl, CURLOPT_POST, 1L); easy_setopt(curl, CURLOPT_POSTFIELDS, testput); - easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(testput)); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)(sizeof(testput) - 1)); result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib1955.c b/tests/libtest/lib1955.c index 79fd92d6ed2c..ff640c20d4b9 100644 --- a/tests/libtest/lib1955.c +++ b/tests/libtest/lib1955.c @@ -42,18 +42,18 @@ static CURLcode test_lib1955(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "test3: 1234"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "Content-Type: application/json"); /* 'name;' user headers with no value are used to send an empty header in the @@ -73,7 +73,7 @@ static CURLcode test_lib1955(const char *URL) curl_slist_append(list, "test_space: t\ts m\t end "); curl_slist_append(list, "tesMixCase: MixCase"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1956.c b/tests/libtest/lib1956.c index c9a4d405e26b..5d569c785b14 100644 --- a/tests/libtest/lib1956.c +++ b/tests/libtest/lib1956.c @@ -42,21 +42,21 @@ static CURLcode test_lib1956(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "X-Xxx-Content-Sha256: " "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1957.c b/tests/libtest/lib1957.c index ea4226303141..b422e48553fa 100644 --- a/tests/libtest/lib1957.c +++ b/tests/libtest/lib1957.c @@ -42,20 +42,20 @@ static CURLcode test_lib1957(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "X-Xxx-Content-Sha256: arbitrary"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1958.c b/tests/libtest/lib1958.c index cae62c239297..dc4ac1cd9a40 100644 --- a/tests/libtest/lib1958.c +++ b/tests/libtest/lib1958.c @@ -42,20 +42,20 @@ static CURLcode test_lib1958(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "X-Xxx-Content-Sha256: \tarbitrary "); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1959.c b/tests/libtest/lib1959.c index 1f6e725856f3..4df8bd71cdd3 100644 --- a/tests/libtest/lib1959.c +++ b/tests/libtest/lib1959.c @@ -42,22 +42,22 @@ static CURLcode test_lib1959(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "xxx"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "X-Xxx-Content-Sha256: " "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1960.c b/tests/libtest/lib1960.c index 2ce9a7540d11..fbb0f7e597a8 100644 --- a/tests/libtest/lib1960.c +++ b/tests/libtest/lib1960.c @@ -63,7 +63,7 @@ static int sockopt_cb(void *clientp, } #ifdef __AMIGA__ -#define my_inet_pton(x, y, z) inet_pton(x, (unsigned char *)y, z) +#define my_inet_pton(x, y, z) inet_pton(x, (unsigned char *)CURL_UNCONST(y), z) #else #define my_inet_pton(x, y, z) inet_pton(x, y, z) #endif @@ -122,16 +122,16 @@ static CURLcode test_lib1960(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, socket_cb); - test_setopt(curl, CURLOPT_OPENSOCKETDATA, &client_fd); - test_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_cb); - test_setopt(curl, CURLOPT_SOCKOPTDATA, NULL); - test_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket_cb); - test_setopt(curl, CURLOPT_CLOSESOCKETDATA, NULL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, socket_cb); + easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &client_fd); + easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_cb); + easy_setopt(curl, CURLOPT_SOCKOPTDATA, NULL); + easy_setopt(curl, CURLOPT_CLOSESOCKETFUNCTION, closesocket_cb); + easy_setopt(curl, CURLOPT_CLOSESOCKETDATA, NULL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); diff --git a/lib/noproxy.h b/tests/libtest/lib1967.c similarity index 71% rename from lib/noproxy.h rename to tests/libtest/lib1967.c index e16c139bb502..e271df28d94d 100644 --- a/lib/noproxy.h +++ b/tests/libtest/lib1967.c @@ -1,5 +1,3 @@ -#ifndef HEADER_CURL_NOPROXY_H -#define HEADER_CURL_NOPROXY_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -23,10 +21,22 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "curl_setup.h" +#include "first.h" -#ifndef CURL_DISABLE_PROXY -bool Curl_check_noproxy(const char *name, const char *no_proxy); -#endif +static CURLcode test_lib1967(const char *URL) +{ + CURLU *u = curl_url(); + (void)URL; + if(u) { + char *url; + curl_url_set(u, CURLUPART_URL, "a.b", CURLU_GUESS_SCHEME); + curl_url_set(u, CURLUPART_URL, "/x", CURLU_NO_GUESS_SCHEME); -#endif /* HEADER_CURL_NOPROXY_H */ + if(!curl_url_get(u, CURLUPART_URL, &url, 0)) { + curl_mprintf("URL %s\n", url); + curl_free(url); + } + curl_url_cleanup(u); + } + return CURLE_OK; +} diff --git a/tests/libtest/lib1970.c b/tests/libtest/lib1970.c index d9b659988c94..8fd69f6f1e4d 100644 --- a/tests/libtest/lib1970.c +++ b/tests/libtest/lib1970.c @@ -42,21 +42,21 @@ static CURLcode test_lib1970(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, 0L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, 0L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1971.c b/tests/libtest/lib1971.c index 4ae7bef2a3c1..81d5956a4483 100644 --- a/tests/libtest/lib1971.c +++ b/tests/libtest/lib1971.c @@ -51,21 +51,21 @@ static CURLcode test_lib1971(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1971_read_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1971_read_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1972.c b/tests/libtest/lib1972.c index 98609bd92963..4315798aecf7 100644 --- a/tests/libtest/lib1972.c +++ b/tests/libtest/lib1972.c @@ -53,20 +53,20 @@ static CURLcode test_lib1972(const char *URL) curl_mime_name(part, "foo"); curl_mime_data(part, "bar", CURL_ZERO_TERMINATED); - test_setopt(curl, CURLOPT_MIMEPOST, mime); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1973.c b/tests/libtest/lib1973.c index 508b16d261d1..778b7af636ba 100644 --- a/tests/libtest/lib1973.c +++ b/tests/libtest/lib1973.c @@ -42,20 +42,20 @@ static CURLcode test_lib1973(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_POSTFIELDS, "post fields\n"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_POSTFIELDS, "post fields\n"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1974.c b/tests/libtest/lib1974.c index ae496591fc2d..84bd430f56e9 100644 --- a/tests/libtest/lib1974.c +++ b/tests/libtest/lib1974.c @@ -41,15 +41,15 @@ static CURLcode test_lib1974(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1975.c b/tests/libtest/lib1975.c index fded2cd8f0ed..05d6e7c67f84 100644 --- a/tests/libtest/lib1975.c +++ b/tests/libtest/lib1975.c @@ -51,23 +51,23 @@ static CURLcode test_lib1975(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, t1975_read_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, t1975_read_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_USERPWD, "xxx"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); list = curl_slist_append(list, "Content-Type: application/json"); if(!list) goto test_cleanup; curl_slist_append(list, "X-Amz-Content-Sha256: " "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib1977.c b/tests/libtest/lib1977.c index 608ba4e8b6d1..a5db34226c15 100644 --- a/tests/libtest/lib1977.c +++ b/tests/libtest/lib1977.c @@ -34,7 +34,7 @@ static CURLcode test_lib1977(const char *URL) global_init(CURL_GLOBAL_ALL); easy_init(curl); - /* first transfer: set just the URL in the first CURLU handle */ + /* first transfer: set the URL in the first CURLU handle */ curl_url_set(curlu, CURLUPART_URL, URL, CURLU_DEFAULT_SCHEME); easy_setopt(curl, CURLOPT_CURLU, curlu); diff --git a/tests/libtest/lib1978.c b/tests/libtest/lib1978.c index 4f3ef11fd064..5a6a44a048cf 100644 --- a/tests/libtest/lib1978.c +++ b/tests/libtest/lib1978.c @@ -42,13 +42,12 @@ static CURLcode test_lib1978(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, 0L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); - test_setopt(curl, CURLOPT_USERPWD, "xxx"); - test_setopt(curl, CURLOPT_HEADER, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, 0L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3"); + easy_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_URL, URL); /* We want to test a couple assumptions here. 1. the merging works with non-adjacent headers @@ -84,11 +83,11 @@ static CURLcode test_lib1978(const char *URL) curl_slist_append(list, "header-some-no-value;"); curl_slist_append(list, "header-some-no-value: value"); - test_setopt(curl, CURLOPT_HTTPHEADER, list); + easy_setopt(curl, CURLOPT_HTTPHEADER, list); if(libtest_arg2) { connect_to = curl_slist_append(connect_to, libtest_arg2); } - test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); + easy_setopt(curl, CURLOPT_CONNECT_TO, connect_to); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib2023.c b/tests/libtest/lib2023.c index 5d99860039c5..b0072b6b122e 100644 --- a/tests/libtest/lib2023.c +++ b/tests/libtest/lib2023.c @@ -35,19 +35,19 @@ static CURLcode send_request(CURL *curl, const char *url, int seq, size_t len = strlen(url) + 4 + 1; char *full_url = curlx_malloc(len); if(!full_url) { - curl_mfprintf(stderr, "Not enough memory for full url\n"); + curl_mfprintf(stderr, "Not enough memory for full URL\n"); return CURLE_OUT_OF_MEMORY; } curl_msnprintf(full_url, len, "%s%04d", url, seq); curl_mfprintf(stderr, "Sending new request %d to %s with credential %s " "(auth %ld)\n", seq, full_url, userpwd, auth_scheme); - test_setopt(curl, CURLOPT_URL, full_url); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_HTTPGET, 1L); - test_setopt(curl, CURLOPT_USERPWD, userpwd); - test_setopt(curl, CURLOPT_HTTPAUTH, auth_scheme); + easy_setopt(curl, CURLOPT_URL, full_url); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HTTPGET, 1L); + easy_setopt(curl, CURLOPT_USERPWD, userpwd); + easy_setopt(curl, CURLOPT_HTTPAUTH, auth_scheme); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib2032.c b/tests/libtest/lib2032.c index e87eb5ecad37..e06a4d704bdb 100644 --- a/tests/libtest/lib2032.c +++ b/tests/libtest/lib2032.c @@ -30,7 +30,7 @@ static CURL *ntlm_curls[MAX_EASY_HANDLES]; static curl_socket_t ntlm_sockets[MAX_EASY_HANDLES]; static CURLcode ntlmcb_res = CURLE_OK; -static size_t callback(char *ptr, size_t size, size_t nmemb, void *data) +static size_t cb2032(char *ptr, size_t size, size_t nmemb, void *data) { ssize_t idx = ((CURL **)data) - ntlm_curls; curl_socket_t sock; @@ -47,7 +47,7 @@ static size_t callback(char *ptr, size_t size, size_t nmemb, void *data) if(result != CURLE_OK) { curl_mfprintf(stderr, "%s:%d curl_easy_getinfo() failed, " "with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); ntlmcb_res = TEST_ERR_MAJOR_BAD; return failure; } @@ -95,7 +95,7 @@ static CURLcode test_lib2032(const char *URL) /* libntlmconnect */ start_test_timing(); if(!full_url) { - curl_mfprintf(stderr, "Not enough memory for full url\n"); + curl_mfprintf(stderr, "Not enough memory for full URL\n"); return TEST_ERR_MAJOR_BAD; } @@ -139,7 +139,7 @@ static CURLcode test_lib2032(const char *URL) /* libntlmconnect */ easy_setopt(ntlm_curls[num_handles], CURLOPT_HTTPGET, 1L); easy_setopt(ntlm_curls[num_handles], CURLOPT_USERPWD, "testuser:testpass"); - easy_setopt(ntlm_curls[num_handles], CURLOPT_WRITEFUNCTION, callback); + easy_setopt(ntlm_curls[num_handles], CURLOPT_WRITEFUNCTION, cb2032); easy_setopt(ntlm_curls[num_handles], CURLOPT_WRITEDATA, (void *)(ntlm_curls + num_handles)); easy_setopt(ntlm_curls[num_handles], CURLOPT_HEADER, 1L); @@ -153,7 +153,7 @@ static CURLcode test_lib2032(const char *URL) /* libntlmconnect */ multi_perform(multi, &running); curl_mfprintf(stderr, "%s:%d running %d state %d\n", - __FILE__, __LINE__, running, state); + __FILE__, __LINE__, running, (int)state); abort_on_test_timeout(); @@ -177,7 +177,8 @@ static CURLcode test_lib2032(const char *URL) /* libntlmconnect */ } state = num_handles < MAX_EASY_HANDLES ? ReadyForNewHandle : NoMoreHandles; - curl_mfprintf(stderr, "%s:%d new state %d\n", __FILE__, __LINE__, state); + curl_mfprintf(stderr, "%s:%d new state %d\n", + __FILE__, __LINE__, (int)state); } multi_timeout(multi, &timeout); diff --git a/tests/libtest/lib2082.c b/tests/libtest/lib2082.c index 87d0f8eada4b..8a404f232740 100644 --- a/tests/libtest/lib2082.c +++ b/tests/libtest/lib2082.c @@ -88,7 +88,8 @@ static CURLcode test_lib2082(const char *URL) /* libprereq */ if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, + curl_easy_strerror(result)); goto test_cleanup; } } diff --git a/tests/libtest/lib2301.c b/tests/libtest/lib2301.c index 8d036682a45a..0af579231e47 100644 --- a/tests/libtest/lib2301.c +++ b/tests/libtest/lib2301.c @@ -86,7 +86,7 @@ static CURLcode test_lib2301(const char *URL) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, t2301_write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl); result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); #if 0 if(result == CURLE_OK) t2301_websocket(curl); @@ -97,6 +97,8 @@ static CURLcode test_lib2301(const char *URL) curl_global_cleanup(); return result; #else - NO_SUPPORT_BUILT_IN + (void)URL; + curl_mfprintf(stderr, "Missing support\n"); + return CURLE_UNSUPPORTED_PROTOCOL; #endif } diff --git a/tests/libtest/lib2302.c b/tests/libtest/lib2302.c index ef8990fc177f..759ea5eafd24 100644 --- a/tests/libtest/lib2302.c +++ b/tests/libtest/lib2302.c @@ -48,7 +48,7 @@ static void flush_data(struct ws_data *wd) curl_mprintf("\n"); if(wd->has_meta) - curl_mprintf("RECFLAGS: %x\n", wd->meta_flags); + curl_mprintf("RECFLAGS: %x\n", (unsigned int)wd->meta_flags); else curl_mfprintf(stderr, "RECFLAGS: NULL\n"); wd->blen = 0; @@ -63,7 +63,7 @@ static size_t add_data(struct ws_data *wd, const char *buf, size_t blen, (meta && meta->flags != wd->meta_flags)) { if(wd->nwrites > 0) flush_data(wd); - wd->has_meta = (meta != NULL); + wd->has_meta = !!meta; wd->meta_flags = meta ? meta->flags : 0; } @@ -102,7 +102,7 @@ static CURLcode test_lib2302(const char *URL) global_init(CURL_GLOBAL_ALL); memset(&ws_data, 0, sizeof(ws_data)); - ws_data.buf = (char *)curlx_calloc(LIB2302_BUFSIZE, 1); + ws_data.buf = curlx_calloc(LIB2302_BUFSIZE, 1); if(ws_data.buf) { curl = curl_easy_init(); if(curl) { @@ -115,7 +115,7 @@ static CURLcode test_lib2302(const char *URL) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, t2302_write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ws_data); result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); /* always cleanup */ curl_easy_cleanup(curl); flush_data(&ws_data); @@ -125,6 +125,8 @@ static CURLcode test_lib2302(const char *URL) curl_global_cleanup(); return result; #else - NO_SUPPORT_BUILT_IN + (void)URL; + curl_mfprintf(stderr, "Missing support\n"); + return CURLE_UNSUPPORTED_PROTOCOL; #endif } diff --git a/tests/libtest/lib2304.c b/tests/libtest/lib2304.c index fd2a29da6c46..e4055315a7d4 100644 --- a/tests/libtest/lib2304.c +++ b/tests/libtest/lib2304.c @@ -34,7 +34,7 @@ static CURLcode recv_any(CURL *curl) return result; curl_mfprintf(stderr, "recv_any: got %zu bytes rflags %x\n", rlen, - meta->flags); + (unsigned int)meta->flags); return CURLE_OK; } @@ -75,7 +75,7 @@ static CURLcode test_lib2304(const char *URL) curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 2L); /* websocket style */ result = curl_easy_perform(curl); - curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() returned %d\n", (int)result); if(result == CURLE_OK) t2304_websocket(curl); @@ -85,6 +85,8 @@ static CURLcode test_lib2304(const char *URL) curl_global_cleanup(); return result; #else - NO_SUPPORT_BUILT_IN + (void)URL; + curl_mfprintf(stderr, "Missing support\n"); + return CURLE_UNSUPPORTED_PROTOCOL; #endif } diff --git a/tests/libtest/lib2308.c b/tests/libtest/lib2308.c index 40d77019d1e9..d582c7135c86 100644 --- a/tests/libtest/lib2308.c +++ b/tests/libtest/lib2308.c @@ -42,7 +42,7 @@ static CURLcode test_lib2308(const char *URL) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb_curl); curl_easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); - curl_mprintf("Returned %d, should be %d.\n", result, CURLE_WRITE_ERROR); + curl_mprintf("Returned %d, should be %d.\n", (int)result, CURLE_WRITE_ERROR); fflush(stdout); curl_easy_cleanup(curl); curl_global_cleanup(); diff --git a/tests/libtest/lib2309.c b/tests/libtest/lib2309.c index c58b7ebbb4c6..212d82aa162d 100644 --- a/tests/libtest/lib2309.c +++ b/tests/libtest/lib2309.c @@ -51,7 +51,8 @@ static CURLcode test_lib2309(const char *URL) curldupe = curl_easy_duphandle(curl); if(curldupe) { result = curl_easy_perform(curldupe); - curl_mprintf("Returned %d, should be %d.\n", result, CURLE_WRITE_ERROR); + curl_mprintf("Returned %d, should be %d.\n", (int)result, + CURLE_WRITE_ERROR); fflush(stdout); curl_easy_cleanup(curldupe); } diff --git a/tests/libtest/lib2405.c b/tests/libtest/lib2405.c index eee6e9123708..412faf45a01e 100644 --- a/tests/libtest/lib2405.c +++ b/tests/libtest/lib2405.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Dmitry Karpov + * Copyright (C) Dmitry Karpov * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -29,8 +29,8 @@ * HTTP1 amd HTTP2 (no multiplexing) two transfers (expected two descriptors), * HTTP2 with multiplexing (expected one descriptors) * Improper inputs to the API result in CURLM_BAD_FUNCTION_ARGUMENT. - * Sending an empty ufds, and size = 0 will return the number of fds needed. - * Sending a non-empty ufds, but smaller than the fds needed will result in a + * Sending an empty ufds, and size = 0 returns the number of fds needed. + * Sending a non-empty ufds, but smaller than the fds needed results in a * CURLM_OUT_OF_MEMORY, and a number of fds that is >= to the number needed. * * It is also expected that all transfers run by multi-handle should complete @@ -43,7 +43,7 @@ #define test_check(expected_fds) \ if(result != CURLE_OK) { \ - curl_mfprintf(stderr, "test failed with code: %d\n", result); \ + curl_mfprintf(stderr, "test failed with code: %d\n", (int)result); \ goto test_cleanup; \ } \ else if(fd_count != (expected_fds)) { \ @@ -53,10 +53,10 @@ goto test_cleanup; \ } -#define test_run_check(option, expected_fds) \ - do { \ - result = test_run(URL, option, &fd_count); \ - test_check(expected_fds); \ +#define test_run_check(option, expected_fds) \ + do { \ + result = test_run(URL, option, &fd_count); \ + test_check(expected_fds); \ } while(0) /* ---------------------------------------------------------------- */ @@ -67,13 +67,6 @@ enum { TEST_USE_HTTP2_MPLEX }; -static size_t emptyWriteFunc(char *ptr, size_t size, size_t nmemb, void *data) -{ - (void)ptr; - (void)data; - return size * nmemb; -} - static CURLcode set_easy(const char *URL, CURL *curl, long option) { CURLcode result = CURLE_OK; @@ -110,7 +103,7 @@ static CURLcode set_easy(const char *URL, CURL *curl, long option) easy_setopt(curl, CURLOPT_HEADER, 1L); /* empty write function */ - easy_setopt(curl, CURLOPT_WRITEFUNCTION, emptyWriteFunc); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); test_cleanup: return result; diff --git a/tests/libtest/lib2412.c b/tests/libtest/lib2412.c new file mode 100644 index 000000000000..79d49a2d7680 --- /dev/null +++ b/tests/libtest/lib2412.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Dmitry Karpov + * + * 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 "first.h" +#include "testtrace.h" + +static CURLcode test_lib2412(const char *URL) +{ + CURLcode result = CURLE_OK; + CURLM *multi = NULL; + CURL *easy = NULL; + CURLMcode rc; + fd_set readFdSet, writeFdSet, exceptFdSet; + int maxFd; + + (void)URL; + global_init(CURL_GLOBAL_ALL); + + multi = curl_multi_init(); + if(!multi) { + curl_mfprintf(stderr, "curl_multi_init() failed\n"); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + easy = curl_easy_init(); + if(!easy) { + curl_mfprintf(stderr, "curl_easy_init() failed\n"); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + debug_config.nohex = TRUE; + debug_config.tracetime = TRUE; + easy_setopt(easy, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(easy, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(easy, CURLOPT_VERBOSE, 1L); + + rc = curl_multi_add_handle(multi, easy); + if(rc) { + curl_mfprintf(stderr, "curl_multi_add_handle() failed: %d\n", rc); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + FD_ZERO(&readFdSet); + FD_ZERO(&writeFdSet); + FD_ZERO(&exceptFdSet); + maxFd = -1; + rc = curl_multi_fdset(multi, &readFdSet, &writeFdSet, &exceptFdSet, + &maxFd); + if(rc) { + curl_mfprintf(stderr, "curl_multi_fdset() failed: %d\n", rc); + result = TEST_ERR_MAJOR_BAD; + goto test_cleanup; + } + + if(maxFd == -1) + curl_mfprintf(stderr, "There are no file descriptors to wait for\n"); + else { + curl_mfprintf(stderr, "libcurl supplied a file descriptor to " + "wait for (maxFd=%d). Waiting now ...\n", maxFd); + result = TEST_ERR_FAILURE; + } + +test_cleanup: + if(easy) { + curl_multi_remove_handle(multi, easy); + curl_easy_cleanup(easy); + } + if(multi) + curl_multi_cleanup(multi); + curl_global_cleanup(); + return result; +} diff --git a/tests/libtest/lib2502.c b/tests/libtest/lib2502.c index 4743afc11996..18382436c63b 100644 --- a/tests/libtest/lib2502.c +++ b/tests/libtest/lib2502.c @@ -74,7 +74,7 @@ static CURLcode test_lib2502(const char *URL) /* go verbose */ debug_config.nohex = TRUE; debug_config.tracetime = FALSE; - test_setopt(curl[i], CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl[i], CURLOPT_DEBUGDATA, &debug_config); easy_setopt(curl[i], CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl[i], CURLOPT_VERBOSE, 1L); /* include headers */ diff --git a/tests/libtest/lib2504.c b/tests/libtest/lib2504.c index 72b965d6e6ed..3b7b61fe8b47 100644 --- a/tests/libtest/lib2504.c +++ b/tests/libtest/lib2504.c @@ -25,13 +25,6 @@ #include "testtrace.h" -static size_t sink2504(char *ptr, size_t size, size_t nmemb, void *ud) -{ - (void)ptr; - (void)ud; - return size * nmemb; -} - static void dump_cookies2504(CURL *h, const char *tag) { struct curl_slist *cookies = NULL; @@ -68,17 +61,17 @@ static CURLcode test_lib2504(const char *URL) hdrs = curl_slist_append(hdrs, "Host: victim.internal"); if(hdrs) { - test_setopt(curl, CURLOPT_WRITEFUNCTION, sink2504); - test_setopt(curl, CURLOPT_COOKIEFILE, ""); - test_setopt(curl, CURLOPT_HTTPHEADER, hdrs); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_COOKIEFILE, ""); + easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); curl_mprintf("req1=%d\n", (int)result); dump_cookies2504(curl, "after request 1"); - test_setopt(curl, CURLOPT_HTTPHEADER, NULL); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPHEADER, NULL); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); curl_mprintf("req2=%d\n", (int)result); diff --git a/tests/libtest/lib2505.c b/tests/libtest/lib2505.c index c17025987418..5d43eec3920f 100644 --- a/tests/libtest/lib2505.c +++ b/tests/libtest/lib2505.c @@ -25,13 +25,6 @@ #include "testtrace.h" -static size_t sink2505(char *ptr, size_t size, size_t nmemb, void *ud) -{ - (void)ptr; - (void)ud; - return size * nmemb; -} - static CURLcode test_lib2505(const char *URL) { CURL *curl; @@ -49,16 +42,16 @@ static CURLcode test_lib2505(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_WRITEFUNCTION, sink2505); - test_setopt(curl, CURLOPT_AUTOREFERER, 1L); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_AUTOREFERER, 1L); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); curl_mprintf("req1=%d\n", (int)result); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 0L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); curl_mprintf("req2=%d\n", (int)result); diff --git a/tests/libtest/lib2506.c b/tests/libtest/lib2506.c index 8b3b3429f901..8779f6c9d5b6 100644 --- a/tests/libtest/lib2506.c +++ b/tests/libtest/lib2506.c @@ -25,13 +25,6 @@ #include "testtrace.h" -static size_t sink2506(char *ptr, size_t size, size_t nmemb, void *ud) -{ - (void)ptr; - (void)ud; - return size * nmemb; -} - static CURLcode test_lib2506(const char *URL) { CURL *curl; @@ -49,17 +42,17 @@ static CURLcode test_lib2506(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_WRITEFUNCTION, sink2506); - test_setopt(curl, CURLOPT_PROXY, URL); - test_setopt(curl, CURLOPT_URL, libtest_arg2); - test_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); - test_setopt(curl, CURLOPT_NETRC_FILE, libtest_arg3); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); + easy_setopt(curl, CURLOPT_PROXY, URL); + easy_setopt(curl, CURLOPT_URL, libtest_arg2); + easy_setopt(curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL); + easy_setopt(curl, CURLOPT_NETRC_FILE, libtest_arg3); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* CURLOPT_UNRESTRICTED_AUTH should not make a difference because the credentials come from netrc */ - test_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1L); + easy_setopt(curl, CURLOPT_UNRESTRICTED_AUTH, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib2700.c b/tests/libtest/lib2700.c index e8199e0364b6..c2bb39e22196 100644 --- a/tests/libtest/lib2700.c +++ b/tests/libtest/lib2700.c @@ -39,7 +39,7 @@ static const char *descr_flags(int flags) return "pong"; if(flags & CURLWS_CLOSE) return "close"; - assert(false); + assert(FALSE); return ""; } @@ -50,14 +50,14 @@ static CURLcode send_header(CURL *curl, int flags, size_t size) retry: result = curl_ws_send(curl, NULL, 0, &nsent, (curl_off_t)size, - flags | CURLWS_OFFSET); + flags | CURLWS_OFFSET); if(result == CURLE_AGAIN) { assert(nsent == 0); goto retry; } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_send() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); assert(nsent == 0); return result; } @@ -86,13 +86,13 @@ static CURLcode recv_header(CURL *curl, int *flags, curl_off_t *offset, } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_recv() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); assert(nread == 0); return result; } assert(nread == 0); - assert(meta != NULL); + assert(meta); assert(meta->flags); assert(meta->offset == 0); @@ -128,7 +128,7 @@ static CURLcode send_chunk(CURL *curl, int flags, const char *buffer, } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_send() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); assert(nsent == 0); return result; } @@ -157,13 +157,13 @@ static CURLcode recv_chunk(CURL *curl, int flags, curl_off_t *offset, } if(result) { curl_mfprintf(stderr, "%s:%d curl_ws_recv() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); assert(nread == 0); return result; } assert(nread <= sizeof(buffer)); - assert(meta != NULL); + assert(meta); assert(meta->flags == flags); assert(meta->offset == *offset); assert(meta->bytesleft == (*bytesleft - (curl_off_t)nread)); @@ -200,7 +200,7 @@ static CURLcode recv_frame(CURL *curl, bool *stop) } if(flags & CURLWS_CLOSE) - *stop = true; + *stop = TRUE; curl_mfprintf(stdout, "\n"); @@ -212,7 +212,7 @@ static CURLcode test_lib2700(const char *URL) { #ifndef CURL_DISABLE_WEBSOCKETS CURLcode result = CURLE_OK; - bool stop = false; + bool stop = FALSE; CURL *curl; global_init(CURL_GLOBAL_ALL); @@ -234,7 +234,7 @@ static CURLcode test_lib2700(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -249,6 +249,8 @@ static CURLcode test_lib2700(const char *URL) curl_global_cleanup(); return result; #else - NO_SUPPORT_BUILT_IN + (void)URL; + curl_mfprintf(stderr, "Missing support\n"); + return CURLE_UNSUPPORTED_PROTOCOL; #endif } diff --git a/tests/libtest/lib3010.c b/tests/libtest/lib3010.c index 7a668b075133..7552b021ec77 100644 --- a/tests/libtest/lib3010.c +++ b/tests/libtest/lib3010.c @@ -39,7 +39,8 @@ static CURLcode test_lib3010(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, + curl_easy_strerror(result)); goto test_cleanup; } curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &follow_url); @@ -50,7 +51,8 @@ static CURLcode test_lib3010(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, + curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib3025.c b/tests/libtest/lib3025.c index 362325311fe6..c7b38e465d12 100644 --- a/tests/libtest/lib3025.c +++ b/tests/libtest/lib3025.c @@ -42,10 +42,10 @@ static CURLcode test_lib3025(const char *URL) } icy = curl_slist_append(icy, "ICY 200 OK"); - test_setopt(curl, CURLOPT_HTTP200ALIASES, icy); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTP200ALIASES, icy); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib3026.c b/tests/libtest/lib3026.c index fb56da7ef846..f072dee282fb 100644 --- a/tests/libtest/lib3026.c +++ b/tests/libtest/lib3026.c @@ -76,7 +76,7 @@ static CURLcode test_lib3026(const char *URL) if(results[i] != CURLE_OK) { curl_mfprintf(stderr, "%s:%d thread[%u]: curl_global_init() failed," "with code %d (%s)\n", __FILE__, __LINE__, - i, results[i], curl_easy_strerror(results[i])); + i, (int)results[i], curl_easy_strerror(results[i])); result = TEST_ERR_MAJOR_BAD; } } @@ -133,7 +133,7 @@ static CURLcode test_lib3026(const char *URL) if(results[i] != CURLE_OK) { curl_mfprintf(stderr, "%s:%d thread[%u]: curl_global_init() failed," "with code %d (%s)\n", __FILE__, __LINE__, - i, results[i], curl_easy_strerror(results[i])); + i, (int)results[i], curl_easy_strerror(results[i])); result = TEST_ERR_MAJOR_BAD; } } diff --git a/tests/libtest/lib3027.c b/tests/libtest/lib3027.c index 845eea239a73..013c13f43ec4 100644 --- a/tests/libtest/lib3027.c +++ b/tests/libtest/lib3027.c @@ -41,7 +41,7 @@ static CURLcode test_lib3027(const char *URL) result = curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime); /* MTDM fails with 550, so filetime should be -1 */ if((result == CURLE_OK) && (filetime != -1)) { - /* we just need to return something which is not CURLE_OK */ + /* we need to return something which is not CURLE_OK */ result = CURLE_UNSUPPORTED_PROTOCOL; } } diff --git a/tests/libtest/lib3033.c b/tests/libtest/lib3033.c index 7fa4731badef..f43c1696ca36 100644 --- a/tests/libtest/lib3033.c +++ b/tests/libtest/lib3033.c @@ -69,7 +69,8 @@ static CURLcode t3033_req_test(CURLM *multi, CURL *curl, result = msg->data.result; if(result != CURLE_OK) { - curl_mfprintf(stderr, "curl_multi_info_read() returned %d\n", result); + curl_mfprintf(stderr, "curl_multi_info_read() returned %d\n", + (int)result); goto test_cleanup; } diff --git a/tests/libtest/lib3034.c b/tests/libtest/lib3034.c index 5c693c5ce854..0dbfee84391f 100644 --- a/tests/libtest/lib3034.c +++ b/tests/libtest/lib3034.c @@ -42,22 +42,22 @@ static CURLcode test_lib3034(const char *URL) global_init(CURL_GLOBAL_ALL); easy_init(curl); - /* This first request will receive a redirect response; deliberately only + /* This first request receives a redirect response; deliberately only * set the CURLOPT_READFUNCTION but not the CURLOPT_SEEKFUNCTION to force a * rewind failure (CURLE_SEND_FAIL_REWIND). */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE, 5L); - test_setopt(curl, CURLOPT_READFUNCTION, t3034_read_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE, 5L); + easy_setopt(curl, CURLOPT_READFUNCTION, t3034_read_cb); result = curl_easy_perform(curl); if(result != CURLE_SEND_FAIL_REWIND) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } @@ -65,8 +65,8 @@ static CURLcode test_lib3034(const char *URL) curl_easy_reset(curl); /* Perform a second request, which should succeed. */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib3100.c b/tests/libtest/lib3100.c index 8d7988af0e49..121d0a8490e4 100644 --- a/tests/libtest/lib3100.c +++ b/tests/libtest/lib3100.c @@ -40,21 +40,21 @@ static CURLcode test_lib3100(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL); - test_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - test_setopt(curl, CURLOPT_USERNAME, "user"); - test_setopt(curl, CURLOPT_PASSWORD, "password"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + easy_setopt(curl, CURLOPT_USERNAME, "user"); + easy_setopt(curl, CURLOPT_PASSWORD, "password"); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); result = curl_easy_perform(curl); if(result != CURLE_OK) { - curl_mfprintf(stderr, "Failed to send DESCRIBE: %d\n", result); + curl_mfprintf(stderr, "Failed to send DESCRIBE: %d\n", (int)result); result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } diff --git a/tests/libtest/lib3101.c b/tests/libtest/lib3101.c index 56fc27467ad5..2e2b45357a7f 100644 --- a/tests/libtest/lib3101.c +++ b/tests/libtest/lib3101.c @@ -40,14 +40,14 @@ static CURLcode test_lib3101(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - test_setopt(curl, CURLOPT_USERNAME, "user"); - test_setopt(curl, CURLOPT_PASSWORD, "password"); - test_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "https"); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + easy_setopt(curl, CURLOPT_USERNAME, "user"); + easy_setopt(curl, CURLOPT_PASSWORD, "password"); + easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "https"); result = curl_easy_perform(curl); if(result != CURLE_OK) { diff --git a/tests/libtest/lib3102.c b/tests/libtest/lib3102.c index 765488f36ec6..59db9e45f163 100644 --- a/tests/libtest/lib3102.c +++ b/tests/libtest/lib3102.c @@ -34,7 +34,7 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) /* Chains with only a single certificate are always in order */ if(cert_info->num_of_certs <= 1) - return true; + return TRUE; /* Enumerate each certificate in the chain */ for(cert = 0; cert < cert_info->num_of_certs; cert++) { @@ -63,12 +63,12 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) if(last_issuer) { /* If the last certificate's issuer matches the current certificate's * subject, then the chain is in order */ - if(strcmp(last_issuer, subject) != 0) { + if(strcmp(last_issuer, subject)) { curl_mfprintf(stderr, "cert %d issuer does not match cert %d subject\n", cert - 1, cert); curl_mfprintf(stderr, "certificate chain is not in order\n"); - return false; + return FALSE; } } } @@ -77,14 +77,7 @@ static bool is_chain_in_order(struct curl_certinfo *cert_info) } curl_mprintf("certificate chain is in order\n"); - return true; -} - -static size_t wrfu(char *ptr, size_t size, size_t nmemb, void *stream) -{ - (void)stream; - (void)ptr; - return size * nmemb; + return TRUE; } static CURLcode test_lib3102(const char *URL) @@ -105,19 +98,19 @@ static CURLcode test_lib3102(const char *URL) } /* Set the HTTPS URL to retrieve. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Capture certificate information */ - test_setopt(curl, CURLOPT_CERTINFO, 1L); + easy_setopt(curl, CURLOPT_CERTINFO, 1L); /* Ignore output */ - test_setopt(curl, CURLOPT_WRITEFUNCTION, wrfu); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); /* No peer verify */ - test_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); - test_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); if(!result || result == CURLE_GOT_NOTHING) { struct curl_certinfo *cert_info = NULL; diff --git a/tests/libtest/lib3103.c b/tests/libtest/lib3103.c index 268f3f09874c..cedb21d28a9a 100644 --- a/tests/libtest/lib3103.c +++ b/tests/libtest/lib3103.c @@ -35,17 +35,17 @@ static CURLcode test_lib3103(const char *URL) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); curl = curl_easy_init(); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_PROXY, URL); - test_setopt(curl, CURLOPT_URL, "http://localhost/"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_PROXY, URL); + easy_setopt(curl, CURLOPT_URL, "http://localhost/"); - test_setopt(curl, CURLOPT_COOKIEFILE, ""); + easy_setopt(curl, CURLOPT_COOKIEFILE, ""); /* Set a cookie without Max-age or Expires */ - test_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: c1=v1; domain=localhost"); + easy_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: c1=v1; domain=localhost"); result = curl_easy_perform(curl); if(result) { diff --git a/tests/libtest/lib3104.c b/tests/libtest/lib3104.c index 4c9b9090f0cd..96ca51205e27 100644 --- a/tests/libtest/lib3104.c +++ b/tests/libtest/lib3104.c @@ -35,16 +35,16 @@ static CURLcode test_lib3104(const char *URL) curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); curl = curl_easy_init(); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_PROXY, URL); - test_setopt(curl, CURLOPT_URL, "http://example.com/"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_PROXY, URL); + easy_setopt(curl, CURLOPT_URL, "http://example.com/"); - test_setopt(curl, CURLOPT_COOKIEFILE, ""); + easy_setopt(curl, CURLOPT_COOKIEFILE, ""); - test_setopt(curl, CURLOPT_COOKIELIST, + easy_setopt(curl, CURLOPT_COOKIELIST, "example.com\tFALSE\t/\tFALSE\t0\tname\tvalue"); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib3207.c b/tests/libtest/lib3207.c index ae2a3a18d1f5..77df591710c8 100644 --- a/tests/libtest/lib3207.c +++ b/tests/libtest/lib3207.c @@ -73,8 +73,8 @@ static unsigned int test_thread(void *ptr) int i; - /* Loop the transfer and cleanup the handle properly every lap. This will - still reuse ssl session since the pool is in the shared object! */ + /* Loop the transfer and cleanup the handle properly every lap. This + still reuses SSL session since the pool is in the shared object. */ for(i = 0; i < PER_THREAD_SIZE; i++) { CURL *curl = curl_easy_init(); if(curl) { @@ -88,7 +88,7 @@ static unsigned int test_thread(void *ptr) curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); /* always cleanup */ diff --git a/tests/libtest/lib500.c b/tests/libtest/lib500.c index 71b1eaed8fe3..786d8360b177 100644 --- a/tests/libtest/lib500.c +++ b/tests/libtest/lib500.c @@ -69,17 +69,17 @@ static CURLcode test_lib500(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); if(libtest_arg3 && !strcmp(libtest_arg3, "activeftp")) - test_setopt(curl, CURLOPT_FTPPORT, "-"); + easy_setopt(curl, CURLOPT_FTPPORT, "-"); if(testnum == 585 || testnum == 586 || testnum == 595 || testnum == 596) setupcallbacks(curl); @@ -108,7 +108,7 @@ static CURLcode test_lib500(const char *URL) &time_starttransfer); curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &time_total); - /* since the timing will always vary we only compare relative + /* since the timing always varies we only compare relative differences between these 5 times */ if(time_namelookup > time_connect) { curl_mfprintf(moo, "namelookup vs connect: %" CURL_FORMAT_CURL_OFF_T @@ -127,10 +127,7 @@ static CURLcode test_lib500(const char *URL) (time_pretransfer / 1000000), (long)(time_pretransfer % 1000000)); } - if(time_posttransfer > time_pretransfer) { - /* counter-intuitive: on a GET request, all bytes are sent *before* - * PRETRANSFER happens. Thus POSTTRANSFER has to be smaller. - * The reverse would be true for a POST/PUT. */ + if(time_pretransfer > time_posttransfer) { curl_mfprintf(moo, "pretransfer vs posttransfer: %" CURL_FORMAT_CURL_OFF_T ".%06ld %" CURL_FORMAT_CURL_OFF_T ".%06ld\n", diff --git a/tests/libtest/lib501.c b/tests/libtest/lib501.c index 358594c0377e..73bf010a1a9c 100644 --- a/tests/libtest/lib501.c +++ b/tests/libtest/lib501.c @@ -42,10 +42,10 @@ static CURLcode test_lib501(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* just verify that setting this to -1 is fine */ - test_setopt(curl, CURLOPT_MAXREDIRS, -1L); + /* verify that setting this to -1 is fine */ + easy_setopt(curl, CURLOPT_MAXREDIRS, -1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib505.c b/tests/libtest/lib505.c index 7ad6894ae88b..7abaacd14ea2 100644 --- a/tests/libtest/lib505.c +++ b/tests/libtest/lib505.c @@ -24,7 +24,7 @@ #include "first.h" /* - * This example shows an FTP upload, with a rename of the file just after + * This example shows an FTP upload, with a rename of the file right after * a successful upload. * * Example based on source code provided by Erick Nuwendam. Thanks! @@ -38,9 +38,8 @@ static CURLcode test_lib505(const char *URL) FILE *hd_src; int hd; curlx_struct_stat file_info; - struct curl_slist *hl; - - struct curl_slist *headerlist = NULL; + struct curl_slist *headerlist; + struct curl_slist *temp; static const char *buf_1 = "RNFR 505"; static const char *buf_2 = "RNTO 505-forreal"; @@ -92,42 +91,42 @@ static CURLcode test_lib505(const char *URL) /* build a list of commands to pass to libcurl */ - hl = curl_slist_append(headerlist, buf_1); - if(!hl) { + headerlist = curl_slist_append(NULL, buf_1); + if(!headerlist) { curl_mfprintf(stderr, "curl_slist_append() failed\n"); curl_easy_cleanup(curl); curl_global_cleanup(); curlx_fclose(hd_src); return TEST_ERR_MAJOR_BAD; } - headerlist = curl_slist_append(hl, buf_2); - if(!headerlist) { + temp = curl_slist_append(headerlist, buf_2); + if(!temp) { curl_mfprintf(stderr, "curl_slist_append() failed\n"); - curl_slist_free_all(hl); + curl_slist_free_all(headerlist); curl_easy_cleanup(curl); curl_global_cleanup(); curlx_fclose(hd_src); return TEST_ERR_MAJOR_BAD; } - headerlist = hl; + headerlist = temp; /* enable uploading */ - test_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* enable verbose */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* specify target */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* pass in that last of FTP commands to run after the transfer */ - test_setopt(curl, CURLOPT_POSTQUOTE, headerlist); + easy_setopt(curl, CURLOPT_POSTQUOTE, headerlist); /* now specify which file to upload */ - test_setopt(curl, CURLOPT_READDATA, hd_src); + easy_setopt(curl, CURLOPT_READDATA, hd_src); /* and give the size of the upload (optional) */ - test_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); /* Now run off and do what you have been told! */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib506.c b/tests/libtest/lib506.c index 0f6d75c9799d..8d36c32ec3c4 100644 --- a/tests/libtest/lib506.c +++ b/tests/libtest/lib506.c @@ -23,8 +23,6 @@ ***************************************************************************/ #include "first.h" -#include "testutil.h" - #define THREADS 2 /* struct containing data of a thread */ @@ -70,7 +68,7 @@ static void t506_test_lock(CURL *curl, curl_lock_data data, pcounter = &user->cookie_counter; break; default: - curl_mfprintf(stderr, "lock: no such data: %d\n", data); + curl_mfprintf(stderr, "lock: no such data: %d\n", (int)data); return; } @@ -109,7 +107,7 @@ static void t506_test_unlock(CURL *curl, curl_lock_data data, void *useptr) locknum = 2; break; default: - curl_mfprintf(stderr, "unlock: no such data: %d\n", data); + curl_mfprintf(stderr, "unlock: no such data: %d\n", (int)data); return; } @@ -158,7 +156,7 @@ static void *t506_test_fire(void *ptr) if(result) { int i = 0; curl_mfprintf(stderr, "perform URL '%s' repeat %d failed, curlcode %d\n", - tdata->url, i, result); + tdata->url, i, (int)result); } curl_mprintf("CLEANUP\n"); @@ -241,21 +239,21 @@ static CURLcode test_lib506(const char *URL) return TEST_ERR_MAJOR_BAD; } curl_mprintf("CURLOPT_SHARE\n"); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); curl_mprintf("CURLOPT_COOKIELIST injected_and_clobbered\n"); - test_setopt(curl, CURLOPT_COOKIELIST, + easy_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: injected_and_clobbered=yes; " "domain=host.foo.com; expires=Sat Feb 2 11:56:27 GMT 2030"); curl_mprintf("CURLOPT_COOKIELIST ALL\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "ALL"); + easy_setopt(curl, CURLOPT_COOKIELIST, "ALL"); curl_mprintf("CURLOPT_COOKIELIST session\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: session=elephants"); + easy_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: session=elephants"); curl_mprintf("CURLOPT_COOKIELIST injected\n"); - test_setopt(curl, CURLOPT_COOKIELIST, + easy_setopt(curl, CURLOPT_COOKIELIST, "Set-Cookie: injected=yes; domain=host.foo.com; " "expires=Sat Feb 2 11:56:27 GMT 2030"); curl_mprintf("CURLOPT_COOKIELIST SESS\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "SESS"); + easy_setopt(curl, CURLOPT_COOKIELIST, "SESS"); curl_mprintf("CLEANUP\n"); curl_easy_cleanup(curl); @@ -285,14 +283,14 @@ static CURLcode test_lib506(const char *URL) url = tutil_suburl(URL, i); headers = sethost(NULL); - test_setopt(curl, CURLOPT_HTTPHEADER, headers); - test_setopt(curl, CURLOPT_URL, url); + easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + easy_setopt(curl, CURLOPT_URL, url); curl_mprintf("CURLOPT_SHARE\n"); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); curl_mprintf("CURLOPT_COOKIEJAR\n"); - test_setopt(curl, CURLOPT_COOKIEJAR, jar); + easy_setopt(curl, CURLOPT_COOKIEJAR, jar); curl_mprintf("CURLOPT_COOKIELIST FLUSH\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "FLUSH"); + easy_setopt(curl, CURLOPT_COOKIELIST, "FLUSH"); curl_mprintf("PERFORM\n"); curl_easy_perform(curl); @@ -312,16 +310,16 @@ static CURLcode test_lib506(const char *URL) } url = tutil_suburl(URL, i); headers = sethost(NULL); - test_setopt(curl, CURLOPT_HTTPHEADER, headers); - test_setopt(curl, CURLOPT_URL, url); + easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + easy_setopt(curl, CURLOPT_URL, url); curl_mprintf("CURLOPT_SHARE\n"); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); curl_mprintf("CURLOPT_COOKIELIST ALL\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "ALL"); + easy_setopt(curl, CURLOPT_COOKIELIST, "ALL"); curl_mprintf("CURLOPT_COOKIEJAR\n"); - test_setopt(curl, CURLOPT_COOKIEFILE, jar); + easy_setopt(curl, CURLOPT_COOKIEFILE, jar); curl_mprintf("CURLOPT_COOKIELIST RELOAD\n"); - test_setopt(curl, CURLOPT_COOKIELIST, "RELOAD"); + easy_setopt(curl, CURLOPT_COOKIELIST, "RELOAD"); result = CURLE_OK; @@ -369,7 +367,8 @@ static CURLcode test_lib506(const char *URL) curl_mprintf("SHARE_CLEANUP\n"); scode = curl_share_cleanup(share); if(scode != CURLSHE_OK) - curl_mfprintf(stderr, "curl_share_cleanup failed, code errno %d\n", scode); + curl_mfprintf(stderr, "curl_share_cleanup failed, code errno %d\n", + (int)scode); curl_mprintf("GLOBAL_CLEANUP\n"); curl_global_cleanup(); diff --git a/tests/libtest/lib508.c b/tests/libtest/lib508.c index e5823c510c14..7ecf2665c48d 100644 --- a/tests/libtest/lib508.c +++ b/tests/libtest/lib508.c @@ -36,13 +36,13 @@ static size_t t508_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) return 0; if(pooh->sizeleft) { - *ptr = pooh->readptr[0]; /* copy one single byte */ - pooh->readptr++; /* advance pointer */ - pooh->sizeleft--; /* less data left */ - return 1; /* we return 1 byte at a time! */ + *ptr = pooh->readptr[0]; /* copy one single byte */ + pooh->readptr++; /* advance pointer */ + pooh->sizeleft--; /* less data left */ + return 1; /* we return 1 byte at a time! */ } - return 0; /* no more data left to deliver */ + return 0; /* no more data left to deliver */ } static CURLcode test_lib508(const char *URL) @@ -56,7 +56,7 @@ static CURLcode test_lib508(const char *URL) struct t508_WriteThis pooh; pooh.readptr = testdata; - pooh.sizeleft = strlen(testdata); + pooh.sizeleft = sizeof(testdata) - 1; if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { curl_mfprintf(stderr, "curl_global_init() failed\n"); @@ -71,27 +71,27 @@ static CURLcode test_lib508(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t508_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t508_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, &pooh); + easy_setopt(curl, CURLOPT_READDATA, &pooh); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib509.c b/tests/libtest/lib509.c index f888bbf156a4..38da36428fc6 100644 --- a/tests/libtest/lib509.c +++ b/tests/libtest/lib509.c @@ -99,7 +99,7 @@ static CURLcode test_lib509(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_USERAGENT, "test509"); /* uses curlx_strdup() */ + easy_setopt(curl, CURLOPT_USERAGENT, "test509"); /* uses curlx_strdup() */ asize = (int)sizeof(a); /* uses curlx_realloc() */ diff --git a/tests/libtest/lib510.c b/tests/libtest/lib510.c index c325dc0bb7e3..a4acbdddb623 100644 --- a/tests/libtest/lib510.c +++ b/tests/libtest/lib510.c @@ -87,32 +87,32 @@ static CURLcode test_lib510(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t510_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t510_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, &pooh); + easy_setopt(curl, CURLOPT_READDATA, &pooh); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* enforce chunked transfer by setting the header */ - test_setopt(curl, CURLOPT_HTTPHEADER, slist); + easy_setopt(curl, CURLOPT_HTTPHEADER, slist); if(testnum == 565) { - test_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - test_setopt(curl, CURLOPT_USERPWD, "foo:bar"); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + easy_setopt(curl, CURLOPT_USERPWD, "foo:bar"); } - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib511.c b/tests/libtest/lib511.c index d87b4805bc7b..7907f500ee3a 100644 --- a/tests/libtest/lib511.c +++ b/tests/libtest/lib511.c @@ -40,10 +40,10 @@ static CURLcode test_lib511(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_FILETIME, 1L); - test_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_FILETIME, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib513.c b/tests/libtest/lib513.c index fb1ead53be92..9ed3f1dd424a 100644 --- a/tests/libtest/lib513.c +++ b/tests/libtest/lib513.c @@ -50,27 +50,27 @@ static CURLcode test_lib513(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, 1L); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 1L); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t513_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t513_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, NULL); + easy_setopt(curl, CURLOPT_READDATA, NULL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib514.c b/tests/libtest/lib514.c index 89db0645821b..1fe9adc8d543 100644 --- a/tests/libtest/lib514.c +++ b/tests/libtest/lib514.c @@ -41,7 +41,7 @@ static CURLcode test_lib514(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Based on a bug report by Niels van Tongeren on June 29, 2004: @@ -52,21 +52,21 @@ static CURLcode test_lib514(const char *URL) */ - test_setopt(curl, CURLOPT_POSTFIELDS, "moo"); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, 3L); - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POSTFIELDS, "moo"); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 3L); + easy_setopt(curl, CURLOPT_POST, 1L); /* this is where transfer 1 would take place, but skip that and change options right away instead */ - test_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Now, we should be making a fine HEAD request */ - /* Perform the request 2, result will get the return code */ + /* Perform the request 2, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib515.c b/tests/libtest/lib515.c index 9dcb17271b35..825cbe56cf8e 100644 --- a/tests/libtest/lib515.c +++ b/tests/libtest/lib515.c @@ -41,11 +41,11 @@ static CURLcode test_lib515(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_POSTFIELDS, NULL); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, 0L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Now, we should be making a zero byte POST request */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib516.c b/tests/libtest/lib516.c index 21d6d945bc0c..61090736b772 100644 --- a/tests/libtest/lib516.c +++ b/tests/libtest/lib516.c @@ -41,10 +41,10 @@ static CURLcode test_lib516(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPPOST, NULL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPPOST, NULL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Now, we should be making a zero byte POST request */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib517.c b/tests/libtest/lib517.c index ef8663c39abd..609a44a06b2b 100644 --- a/tests/libtest/lib517.c +++ b/tests/libtest/lib517.c @@ -73,9 +73,9 @@ static CURLcode test_lib517(const char *URL) { "1-Jan-2003 00:00:00 GMT", 1041379200 }, { "1-Jan-2003 00:00:00 GMT", 1041379200 }, { "Wed,18-Apr-07 22:50:12 GMT", 1176936612 }, - { "WillyWonka , 18-Apr-07 22:50:12 GMT", -1 }, - { "WillyWonka , 18-Apr-07 22:50:12", -1 }, - { "WillyWonka , 18-apr-07 22:50:12", -1 }, + { "BillyWonka , 18-Apr-07 22:50:12 GMT", -1 }, + { "BillyWonka , 18-Apr-07 22:50:12", -1 }, + { "BillyWonka , 18-apr-07 22:50:12", -1 }, { "Mon, 18-Apr-1977 22:50:13 GMT", 230251813 }, { "Mon, 18-Apr-77 22:50:13 GMT", 230251813 }, { "Sat, 15-Apr-17\"21:01:22\"GMT", 1492290082 }, diff --git a/tests/libtest/lib518.c b/tests/libtest/lib518.c index 5c50ca5cbf0a..bef952bac7f6 100644 --- a/tests/libtest/lib518.c +++ b/tests/libtest/lib518.c @@ -23,7 +23,7 @@ ***************************************************************************/ #include "first.h" -#include "testutil.h" +#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) #define T518_SAFETY_MARGIN 16 @@ -36,8 +36,6 @@ #define DEV_NULL "/dev/null" #endif -#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) - static int *t518_testfd = NULL; static struct rlimit t518_num_open; static char t518_msgbuff[256]; @@ -60,8 +58,7 @@ static void t518_close_file_descriptors(void) t518_num_open.rlim_cur++) if(t518_testfd[t518_num_open.rlim_cur] > 0) curlx_close(t518_testfd[t518_num_open.rlim_cur]); - curlx_free(t518_testfd); - t518_testfd = NULL; + curlx_safefree(t518_testfd); } static int t518_fopen_works(void) @@ -100,7 +97,7 @@ static int t518_test_rlimit(int keep_open) /* get initial open file limits */ - if(getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(getrlimit(RLIMIT_NOFILE, &rl)) { t518_store_errmsg("getrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); return -1; @@ -126,7 +123,7 @@ static int t518_test_rlimit(int keep_open) * limit. Due to some other system limit the soft limit * might not be raised up to the hard limit. So from this * point the resulting soft limit is our limit. Trying to - * open more than soft limit file descriptors will fail. + * open more than soft limit file descriptors does fail. */ if(rl.rlim_cur != rl.rlim_max) { @@ -136,8 +133,8 @@ static int t518_test_rlimit(int keep_open) (rl.rlim_cur < OPEN_MAX)) { curl_mfprintf(stderr, "raising soft limit up to OPEN_MAX\n"); rl.rlim_cur = OPEN_MAX; - if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { - /* on failure do not abort just issue a warning */ + if(setrlimit(RLIMIT_NOFILE, &rl)) { + /* on failure do not abort, only issue a warning */ t518_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); t518_msgbuff[0] = '\0'; @@ -147,8 +144,8 @@ static int t518_test_rlimit(int keep_open) curl_mfprintf(stderr, "raising soft limit up to hard limit\n"); rl.rlim_cur = rl.rlim_max; - if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { - /* on failure do not abort just issue a warning */ + if(setrlimit(RLIMIT_NOFILE, &rl)) { + /* on failure do not abort, only issue a warning */ t518_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); t518_msgbuff[0] = '\0'; @@ -156,7 +153,7 @@ static int t518_test_rlimit(int keep_open) /* get current open file limits */ - if(getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(getrlimit(RLIMIT_NOFILE, &rl)) { t518_store_errmsg("getrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); return -3; @@ -239,7 +236,7 @@ static int t518_test_rlimit(int keep_open) for(i = 0; i < nitems; i++) memchunk[i] = -1; - /* set the number of file descriptors we will try to open */ + /* set the number of file descriptors we try to open */ t518_num_open.rlim_max = NUM_OPEN; @@ -289,8 +286,7 @@ static int t518_test_rlimit(int keep_open) curl_msnprintf(strbuff, sizeof(strbuff), "opening of %s failed", DEV_NULL); t518_store_errmsg(strbuff, errno); curl_mfprintf(stderr, "%s\n", t518_msgbuff); - curlx_free(t518_testfd); - t518_testfd = NULL; + curlx_safefree(t518_testfd); curlx_free(memchunk); return -8; } @@ -330,8 +326,7 @@ static int t518_test_rlimit(int keep_open) t518_testfd[t518_num_open.rlim_cur] >= 0; t518_num_open.rlim_cur++) curlx_close(t518_testfd[t518_num_open.rlim_cur]); - curlx_free(t518_testfd); - t518_testfd = NULL; + curlx_safefree(t518_testfd); curlx_free(memchunk); return -9; } @@ -347,7 +342,7 @@ static int t518_test_rlimit(int keep_open) * greater than FD_SETSIZE. In any case, macro VERIFY_SOCK * in lib/select.c enforces this check and protects libcurl * from a possible crash. The effect of this protection - * is that test 518 will always fail, since the actual + * is that test 518 always fails, since the actual * call to select() never takes place. We skip test 518 * with an indication that select limit would be exceeded. */ @@ -452,8 +447,8 @@ static CURLcode test_lib518(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib519.c b/tests/libtest/lib519.c index 3ddc93541b46..30aee83ff5b5 100644 --- a/tests/libtest/lib519.c +++ b/tests/libtest/lib519.c @@ -40,17 +40,17 @@ static CURLcode test_lib519(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_USERPWD, "monster:underbed"); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_USERPWD, "monster:underbed"); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* get first page */ result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_USERPWD, "anothermonster:inwardrobe"); + easy_setopt(curl, CURLOPT_USERPWD, "anothermonster:inwardrobe"); /* get second page */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib520.c b/tests/libtest/lib520.c index 8987c9db6030..9da55e02737f 100644 --- a/tests/libtest/lib520.c +++ b/tests/libtest/lib520.c @@ -40,9 +40,9 @@ static CURLcode test_lib520(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_FILETIME, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_FILETIME, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib521.c b/tests/libtest/lib521.c index 002d07292374..1469d6dc0bad 100644 --- a/tests/libtest/lib521.c +++ b/tests/libtest/lib521.c @@ -44,10 +44,10 @@ static CURLcode test_lib521(const char *URL) return result; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PORT, (long)port); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PORT, (long)port); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib523.c b/tests/libtest/lib523.c index 40a2d3f741a8..ecc3e0ccceea 100644 --- a/tests/libtest/lib523.c +++ b/tests/libtest/lib523.c @@ -40,11 +40,11 @@ static CURLcode test_lib523(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PORT, 19999L); - test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PORT, 19999L); + easy_setopt(curl, CURLOPT_USERPWD, "xxx:yyy"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib524.c b/tests/libtest/lib524.c index 3d5ac1ff4beb..66ada8b0fa29 100644 --- a/tests/libtest/lib524.c +++ b/tests/libtest/lib524.c @@ -40,9 +40,9 @@ static CURLcode test_lib524(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib525.c b/tests/libtest/lib525.c index 88ea084b9a70..caedbde9350f 100644 --- a/tests/libtest/lib525.c +++ b/tests/libtest/lib525.c @@ -85,7 +85,7 @@ static CURLcode test_lib525(const char *URL) /* NOTE: if you want this code to work on Windows with libcurl as a DLL, you MUST also provide a read callback with CURLOPT_READFUNCTION. Failing to - do so will give you a crash since a DLL may not use the variable's memory + do so gives you a crash since a DLL may not use the variable's memory when passed in to it from an app like this. */ /* Set the size of the file to upload (optional). If you give a *_LARGE diff --git a/tests/libtest/lib526.c b/tests/libtest/lib526.c index 2a3461a80aa2..23fbe727c461 100644 --- a/tests/libtest/lib526.c +++ b/tests/libtest/lib526.c @@ -147,7 +147,7 @@ static CURLcode test_lib526(const char *URL) /* Upon non-failure test flow the easy's have already been cleanup'ed. In case there is a failure we arrive here with easy's that have not been cleanup'ed yet, in this case we have to cleanup them or otherwise these - will be leaked, let's use undocumented cleanup sequence - type UB */ + leak, let's use undocumented cleanup sequence - type UB */ if(result != CURLE_OK) for(i = 0; i < CURL_ARRAYSIZE(curl); i++) diff --git a/tests/libtest/lib530.c b/tests/libtest/lib530.c index 31cef1b702f6..bddb857be0f6 100644 --- a/tests/libtest/lib530.c +++ b/tests/libtest/lib530.c @@ -29,6 +29,7 @@ */ #include "first.h" +#include "testtrace.h" static struct t530_ctx { int socket_calls; @@ -203,8 +204,8 @@ static int t530_checkForCompletion(CURLM *multi, int *success) *success = 0; } else { - curl_mfprintf(stderr, "%s got an unexpected message from curl: %i\n", - t530_tag(), message->msg); + curl_mfprintf(stderr, "%s got an unexpected message from curl: %d\n", + t530_tag(), (int)message->msg); result = 1; *success = 0; } @@ -247,7 +248,7 @@ static CURLMcode socket_action(CURLM *multi, curl_socket_t s, int evBitmask, CURLMcode mresult = curl_multi_socket_action(multi, s, evBitmask, &numhandles); if(mresult != CURLM_OK) { - curl_mfprintf(stderr, "%s curl error on %s (%i) %s\n", + curl_mfprintf(stderr, "%s curl error on %s (%d) %s\n", t530_tag(), info, mresult, curl_multi_strerror(mresult)); } return mresult; @@ -300,6 +301,8 @@ static CURLcode testone(const char *URL, int timer_fail_at, int socket_fail_at) easy_setopt(curl, CURLOPT_URL, URL); /* go verbose */ + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); multi_init(multi); @@ -394,23 +397,23 @@ static CURLcode test_lib530(const char *URL) callback calls */ result = testone(URL, 0, 0); /* no callback fails */ if(result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); result = testone(URL, 1, 0); /* fail 1st call to timer callback */ if(!result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); result = testone(URL, 2, 0); /* fail 2nd call to timer callback */ if(!result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); result = testone(URL, 0, 2); /* fail 2nd call to socket callback */ if(!result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); result = testone(URL, 0, 3); /* fail 3rd call to socket callback */ if(!result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), (int)result); return CURLE_OK; } diff --git a/tests/libtest/lib536.c b/tests/libtest/lib536.c index 69208d1dcfde..e25922062ad4 100644 --- a/tests/libtest/lib536.c +++ b/tests/libtest/lib536.c @@ -37,7 +37,7 @@ static CURLcode test_lib536(const char *URL) CURL *curl; struct curl_slist *host = NULL; - static const char *url_with_proxy = "http://usingproxy.com/"; + static const char *url_with_proxy = "http://usingproxy.test/"; const char *url_without_proxy = libtest_arg2; if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { @@ -56,16 +56,16 @@ static CURLcode test_lib536(const char *URL) if(!host) goto test_cleanup; - test_setopt(curl, CURLOPT_RESOLVE, host); - test_setopt(curl, CURLOPT_PROXY, URL); - test_setopt(curl, CURLOPT_URL, url_with_proxy); - test_setopt(curl, CURLOPT_NOPROXY, "goingdirect.com"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_RESOLVE, host); + easy_setopt(curl, CURLOPT_PROXY, URL); + easy_setopt(curl, CURLOPT_URL, url_with_proxy); + easy_setopt(curl, CURLOPT_NOPROXY, "goingdirect.test"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); if(!result) { proxystat(curl); - test_setopt(curl, CURLOPT_URL, url_without_proxy); + easy_setopt(curl, CURLOPT_URL, url_without_proxy); result = curl_easy_perform(curl); if(!result) proxystat(curl); diff --git a/tests/libtest/lib537.c b/tests/libtest/lib537.c index 82b92a02adc7..5f99b4f006f1 100644 --- a/tests/libtest/lib537.c +++ b/tests/libtest/lib537.c @@ -23,7 +23,7 @@ ***************************************************************************/ #include "first.h" -#include "testutil.h" +#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) #define T537_SAFETY_MARGIN 11 @@ -33,8 +33,6 @@ #define DEV_NULL "/dev/null" #endif -#if defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT) - static int *t537_testfd = NULL; static struct rlimit t537_num_open; static char t537_msgbuff[256]; @@ -57,8 +55,7 @@ static void t537_close_file_descriptors(void) t537_num_open.rlim_cur++) if(t537_testfd[t537_num_open.rlim_cur] > 0) curlx_close(t537_testfd[t537_num_open.rlim_cur]); - curlx_free(t537_testfd); - t537_testfd = NULL; + curlx_safefree(t537_testfd); } static int t537_fopen_works(void) @@ -97,7 +94,7 @@ static int t537_test_rlimit(int keep_open) /* get initial open file limits */ - if(getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(getrlimit(RLIMIT_NOFILE, &rl)) { t537_store_errmsg("getrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); return -1; @@ -127,7 +124,7 @@ static int t537_test_rlimit(int keep_open) * limit. Due to some other system limit the soft limit * might not be raised up to the hard limit. So from this * point the resulting soft limit is our limit. Trying to - * open more than soft limit file descriptors will fail. + * open more than soft limit file descriptors does fail. */ if(rl.rlim_cur != rl.rlim_max) { @@ -137,8 +134,8 @@ static int t537_test_rlimit(int keep_open) (rl.rlim_cur < OPEN_MAX)) { curl_mfprintf(stderr, "raising soft limit up to OPEN_MAX\n"); rl.rlim_cur = OPEN_MAX; - if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { - /* on failure do not abort just issue a warning */ + if(setrlimit(RLIMIT_NOFILE, &rl)) { + /* on failure do not abort, only issue a warning */ t537_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); t537_msgbuff[0] = '\0'; @@ -148,8 +145,8 @@ static int t537_test_rlimit(int keep_open) curl_mfprintf(stderr, "raising soft limit up to hard limit\n"); rl.rlim_cur = rl.rlim_max; - if(setrlimit(RLIMIT_NOFILE, &rl) != 0) { - /* on failure do not abort just issue a warning */ + if(setrlimit(RLIMIT_NOFILE, &rl)) { + /* on failure do not abort, only issue a warning */ t537_store_errmsg("setrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); t537_msgbuff[0] = '\0'; @@ -157,7 +154,7 @@ static int t537_test_rlimit(int keep_open) /* get current open file limits */ - if(getrlimit(RLIMIT_NOFILE, &rl) != 0) { + if(getrlimit(RLIMIT_NOFILE, &rl)) { t537_store_errmsg("getrlimit() failed", errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); return -3; @@ -176,10 +173,10 @@ static int t537_test_rlimit(int keep_open) /* * test 537 is all about testing libcurl functionality * when the system has nearly exhausted the number of - * available file descriptors. Test 537 will try to run + * available file descriptors. Test 537 tries to run * with a small number of file descriptors available. * This implies that any file descriptor which is open - * when the test runs will have a number in the high range + * when the test runs does have a number in the high range * of whatever the system supports. */ @@ -219,7 +216,7 @@ static int t537_test_rlimit(int keep_open) for(i = 0; i < nitems; i++) memchunk[i] = -1; - /* set the number of file descriptors we will try to open */ + /* set the number of file descriptors we try to open */ #ifdef RLIM_INFINITY if((rl.rlim_cur > 0) && (rl.rlim_cur != RLIM_INFINITY)) { @@ -291,8 +288,7 @@ static int t537_test_rlimit(int keep_open) curl_msnprintf(strbuff, sizeof(strbuff), "opening of %s failed", DEV_NULL); t537_store_errmsg(strbuff, errno); curl_mfprintf(stderr, "%s\n", t537_msgbuff); - curlx_free(t537_testfd); - t537_testfd = NULL; + curlx_safefree(t537_testfd); curlx_free(memchunk); return -7; } @@ -362,7 +358,7 @@ static int t537_test_rlimit(int keep_open) * greater than FD_SETSIZE. In any case, macro VERIFY_SOCK * in lib/select.c enforces this check and protects libcurl * from a possible crash. The effect of this protection - * is that test 537 will always fail, since the actual + * is that test 537 always fails, since the actual * call to select() never takes place. We skip test 537 * with an indication that select limit would be exceeded. */ @@ -467,8 +463,8 @@ static CURLcode test_lib537(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib539.c b/tests/libtest/lib539.c index 631718be053a..b6719ea53a54 100644 --- a/tests/libtest/lib539.c +++ b/tests/libtest/lib539.c @@ -45,15 +45,15 @@ static CURLcode test_lib539(const char *URL) /* * Begin with curl set to use a single CWD to the URL's directory. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); result = curl_easy_perform(curl); if(result == CURLE_OK) { /* * Change the FTP_FILEMETHOD option to use full paths rather than a CWD - * command. Use an innocuous QUOTE command, after which curl will CWD to + * command. Use an innocuous QUOTE command, after which curl does CWD to * ftp_conn->entrypath and then (on the next call to ftp_statemach_act) * find a non-zero ftpconn->dirdepth even though no directories are stored * in the ftpconn->dirs array (after a call to freedirs). @@ -67,9 +67,9 @@ static CURLcode test_lib539(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, libtest_arg2); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); - test_setopt(curl, CURLOPT_QUOTE, slist); + easy_setopt(curl, CURLOPT_URL, libtest_arg2); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_QUOTE, slist); result = curl_easy_perform(curl); } diff --git a/tests/libtest/lib540.c b/tests/libtest/lib540.c index b8f2d3d8a486..ec9d3da7c95b 100644 --- a/tests/libtest/lib540.c +++ b/tests/libtest/lib540.c @@ -158,7 +158,7 @@ static CURLcode loop(int num, CURLM *multi, const char *url, if(msg->msg == CURLMSG_DONE) { size_t i; CURL *curl = msg->easy_handle; - curl_mfprintf(stderr, "R: %d - %s\n", msg->data.result, + curl_mfprintf(stderr, "R: %d - %s\n", (int)msg->data.result, curl_easy_strerror(msg->data.result)); curl_multi_remove_handle(multi, curl); curl_easy_cleanup(curl); @@ -170,7 +170,7 @@ static CURLcode loop(int num, CURLM *multi, const char *url, } } else - curl_mfprintf(stderr, "E: CURLMsg (%d)\n", msg->msg); + curl_mfprintf(stderr, "E: CURLMsg (%d)\n", (int)msg->msg); } res_test_timedout(); diff --git a/tests/libtest/lib541.c b/tests/libtest/lib541.c index 6cd8e3dce2de..35e9d5a0cda3 100644 --- a/tests/libtest/lib541.c +++ b/tests/libtest/lib541.c @@ -82,16 +82,16 @@ static CURLcode test_lib541(const char *URL) } /* enable uploading */ - test_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); /* enable verbose */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* specify target */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* now specify which file to upload */ - test_setopt(curl, CURLOPT_READDATA, hd_src); + easy_setopt(curl, CURLOPT_READDATA, hd_src); /* Now run off and do what you have been told! */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib542.c b/tests/libtest/lib542.c index fc71ecfdf074..fe3eb9cd1365 100644 --- a/tests/libtest/lib542.c +++ b/tests/libtest/lib542.c @@ -46,16 +46,16 @@ static CURLcode test_lib542(const char *URL) } /* enable verbose */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* enable NOBODY */ - test_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); /* disable HEADER */ - test_setopt(curl, CURLOPT_HEADER, 0L); + easy_setopt(curl, CURLOPT_HEADER, 0L); /* specify target */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now run off and do what you have been told! */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib544.c b/tests/libtest/lib544.c index 6ceac095d72f..02dc11f39bb3 100644 --- a/tests/libtest/lib544.c +++ b/tests/libtest/lib544.c @@ -52,15 +52,15 @@ static CURLcode test_lib544(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); if(testnum == 545) - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(teststring)); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(teststring)); - test_setopt(curl, CURLOPT_COPYPOSTFIELDS, teststring); + easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, teststring); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ /* Update the original data to detect non-copy. */ curlx_strcopy(teststring, sizeof(teststring), "FAIL", strlen("FAIL")); diff --git a/tests/libtest/lib547.c b/tests/libtest/lib547.c index dd0f787ee654..e44c16b55bb1 100644 --- a/tests/libtest/lib547.c +++ b/tests/libtest/lib547.c @@ -80,28 +80,28 @@ static CURLcode test_lib547(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); if(testnum == 548) { /* set the data to POST with a mere pointer to a null-terminated string */ - test_setopt(curl, CURLOPT_POSTFIELDS, t547_uploadthis); + easy_setopt(curl, CURLOPT_POSTFIELDS, t547_uploadthis); } else { /* 547 style, which means reading the POST data from a callback */ - test_setopt(curl, CURLOPT_IOCTLFUNCTION, t547_ioctl_callback); - test_setopt(curl, CURLOPT_IOCTLDATA, &counter); + easy_setopt(curl, CURLOPT_IOCTLFUNCTION, t547_ioctl_callback); + easy_setopt(curl, CURLOPT_IOCTLDATA, &counter); - test_setopt(curl, CURLOPT_READFUNCTION, t547_read_cb); - test_setopt(curl, CURLOPT_READDATA, &counter); + easy_setopt(curl, CURLOPT_READFUNCTION, t547_read_cb); + easy_setopt(curl, CURLOPT_READDATA, &counter); /* We CANNOT do the POST fine without setting the size (or choose chunked)! */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)t547_datalen); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)t547_datalen); } - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_PROXYUSERPWD, libtest_arg3); - test_setopt(curl, CURLOPT_PROXYAUTH, + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_PROXYUSERPWD, libtest_arg3); + easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib549.c b/tests/libtest/lib549.c index e7c5023cb773..fb718988b20f 100644 --- a/tests/libtest/lib549.c +++ b/tests/libtest/lib549.c @@ -45,13 +45,13 @@ static CURLcode test_lib549(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_PROXY_TRANSFER_MODE, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_PROXY_TRANSFER_MODE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); if(libtest_arg3) { /* enable ASCII/text mode */ - test_setopt(curl, CURLOPT_TRANSFERTEXT, 1L); + easy_setopt(curl, CURLOPT_TRANSFERTEXT, 1L); } result = curl_easy_perform(curl); diff --git a/tests/libtest/lib552.c b/tests/libtest/lib552.c index fda2eee2c17e..69222d217fde 100644 --- a/tests/libtest/lib552.c +++ b/tests/libtest/lib552.c @@ -79,35 +79,35 @@ static CURLcode test_lib552(const char *URL) global_init(CURL_GLOBAL_ALL); easy_init(curl); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); /* the DEBUGFUNCTION has no effect until we enable VERBOSE */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* setup repeated data string */ for(i = 0; i < sizeof(databuf); ++i) databuf[i] = fill[i % sizeof(fill)]; /* Post */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Setup read callback */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(databuf)); - test_setopt(curl, CURLOPT_READFUNCTION, t552_read_cb); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)sizeof(databuf)); + easy_setopt(curl, CURLOPT_READFUNCTION, t552_read_cb); /* Write callback */ - test_setopt(curl, CURLOPT_WRITEFUNCTION, t552_write_cb); + easy_setopt(curl, CURLOPT_WRITEFUNCTION, t552_write_cb); /* Ioctl function */ - test_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_callback); + easy_setopt(curl, CURLOPT_IOCTLFUNCTION, ioctl_callback); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Accept any auth. But for this bug configure proxy with DIGEST, basic might work too, not NTLM */ - test_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_ANY); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib553.c b/tests/libtest/lib553.c index 75844144bd38..dc812aa4a42a 100644 --- a/tests/libtest/lib553.c +++ b/tests/libtest/lib553.c @@ -89,13 +89,13 @@ static CURLcode test_lib553(const char *URL) goto test_cleanup; headerlist = hl; - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HTTPHEADER, headerlist); - test_setopt(curl, CURLOPT_POST, 1L); - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)POSTLEN); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_READFUNCTION, myreadfunc); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + easy_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)POSTLEN); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_READFUNCTION, myreadfunc); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib554.c b/tests/libtest/lib554.c index 7b882959ddaa..98a7ba7fdcb1 100644 --- a/tests/libtest/lib554.c +++ b/tests/libtest/lib554.c @@ -69,7 +69,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) struct t554_WriteThis pooh2; pooh.readptr = testdata; - pooh.sizeleft = strlen(testdata); + pooh.sizeleft = sizeof(testdata) - 1; /* Fill in the file upload field */ if(oldstyle) { @@ -93,13 +93,13 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) } if(formrc) - curl_mprintf("curl_formadd(1) = %d\n", formrc); + curl_mprintf("curl_formadd(1) = %d\n", (int)formrc); /* Now add the same data with another name and make it not look like a file upload but still using the callback */ pooh2.readptr = testdata; - pooh2.sizeleft = strlen(testdata); + pooh2.sizeleft = sizeof(testdata) - 1; /* Fill in the file upload field */ formrc = curl_formadd(&formpost, @@ -110,7 +110,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(2) = %d\n", formrc); + curl_mprintf("curl_formadd(2) = %d\n", (int)formrc); /* Fill in the filename field */ formrc = curl_formadd(&formpost, @@ -119,7 +119,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) CURLFORM_COPYCONTENTS, "postit2.c", CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(3) = %d\n", formrc); + curl_mprintf("curl_formadd(3) = %d\n", (int)formrc); /* Fill in a submit field too */ formrc = curl_formadd(&formpost, @@ -130,7 +130,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(4) = %d\n", formrc); + curl_mprintf("curl_formadd(4) = %d\n", (int)formrc); formrc = curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "somename", @@ -140,7 +140,7 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(5) = %d\n", formrc); + curl_mprintf("curl_formadd(5) = %d\n", (int)formrc); curl = curl_easy_init(); if(!curl) { @@ -151,32 +151,32 @@ static CURLcode t554_test_once(const char *URL, bool oldstyle) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)pooh.sizeleft); /* we want to use our own read function */ if(testnum == 587) { - test_setopt(curl, CURLOPT_READFUNCTION, t587_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t587_read_cb); } else { - test_setopt(curl, CURLOPT_READFUNCTION, t554_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t554_read_cb); } /* send a multi-part formpost */ - test_setopt(curl, CURLOPT_HTTPPOST, formpost); + easy_setopt(curl, CURLOPT_HTTPPOST, formpost); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib556.c b/tests/libtest/lib556.c index f71ec5e6adb9..1bc4638b9b29 100644 --- a/tests/libtest/lib556.c +++ b/tests/libtest/lib556.c @@ -41,9 +41,9 @@ static CURLcode test_lib556(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); again: diff --git a/tests/libtest/lib557.c b/tests/libtest/lib557.c index 514e71cd3c75..e7fb499fb829 100644 --- a/tests/libtest/lib557.c +++ b/tests/libtest/lib557.c @@ -415,7 +415,7 @@ static int test_unsigned_long_formatting(void) int num_ulong_tests = 0; int failed = 0; -#if (SIZEOF_LONG == 4) +#if SIZEOF_LONG == 4 i = 1; ul_test[i].num = 0xFFFFFFFFUL; ul_test[i].expected = "4294967295"; i++; ul_test[i].num = 0xFFFF0000UL; ul_test[i].expected = "4294901760"; @@ -449,7 +449,7 @@ static int test_unsigned_long_formatting(void) num_ulong_tests = i; -#elif (SIZEOF_LONG == 8) +#elif SIZEOF_LONG == 8 /* !checksrc! disable LONGLINE all */ i = 1; ul_test[i].num = 0xFFFFFFFFFFFFFFFFUL; ul_test[i].expected = "18446744073709551615"; i++; ul_test[i].num = 0xFFFFFFFF00000000UL; ul_test[i].expected = "18446744069414584320"; @@ -541,7 +541,7 @@ static int test_signed_long_formatting(void) int num_slong_tests = 0; int failed = 0; -#if (SIZEOF_LONG == 4) +#if SIZEOF_LONG == 4 i = 1; sl_test[i].num = 0x7FFFFFFFL; sl_test[i].expected = "2147483647"; i++; sl_test[i].num = 0x7FFFFFFEL; sl_test[i].expected = "2147483646"; @@ -608,7 +608,7 @@ static int test_signed_long_formatting(void) num_slong_tests = i; -#elif (SIZEOF_LONG == 8) +#elif SIZEOF_LONG == 8 i = 1; sl_test[i].num = 0x7FFFFFFFFFFFFFFFL; sl_test[i].expected = "9223372036854775807"; i++; sl_test[i].num = 0x7FFFFFFFFFFFFFFEL; sl_test[i].expected = "9223372036854775806"; @@ -1186,11 +1186,11 @@ static int test_oct_hex_formatting(void) 0xFABC1230U, 0xFABC1230U, 0xFABC1230U, 1234U); errors += string_check(buf, "37257011060 fabc1230 FABC1230 +2322"); -#if (SIZEOF_LONG == 4) +#if SIZEOF_LONG == 4 curl_msnprintf(buf, sizeof(buf), "%lo %lx %lX %+lo", 0xFABC1230UL, 0xFABC1230UL, 0xFABC1230UL, 1234UL); errors += string_check(buf, "37257011060 fabc1230 FABC1230 +2322"); -#elif (SIZEOF_LONG == 8) +#elif SIZEOF_LONG == 8 curl_msnprintf(buf, sizeof(buf), "%lo %lx %lX %+lo", 0xFABCDEF123456780UL, 0xFABCDEF123456780UL, 0xFABCDEF123456780UL, 1234UL); diff --git a/tests/libtest/lib559.c b/tests/libtest/lib559.c index 1f14b7df435a..27a8cb9c22f3 100644 --- a/tests/libtest/lib559.c +++ b/tests/libtest/lib559.c @@ -40,9 +40,9 @@ static CURLcode test_lib559(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_BUFFERSIZE, 1L); /* the smallest! */ + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_BUFFERSIZE, 1L); /* the smallest! */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib562.c b/tests/libtest/lib562.c index bc3bcad7e144..5cec3f7472dc 100644 --- a/tests/libtest/lib562.c +++ b/tests/libtest/lib562.c @@ -53,13 +53,13 @@ static CURLcode test_lib562(const char *URL) } /* enable verbose */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* set port number */ - test_setopt(curl, CURLOPT_PORT, (long)port); + easy_setopt(curl, CURLOPT_PORT, (long)port); /* specify target */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now run off and do what you have been told! */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib566.c b/tests/libtest/lib566.c index 3b30092b289e..9042894618a4 100644 --- a/tests/libtest/lib566.c +++ b/tests/libtest/lib566.c @@ -42,8 +42,8 @@ static CURLcode test_lib566(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib567.c b/tests/libtest/lib567.c index ac4516e63ed8..dc17f53b0ba4 100644 --- a/tests/libtest/lib567.c +++ b/tests/libtest/lib567.c @@ -45,17 +45,17 @@ static CURLcode test_lib567(const char *URL) } /* Dump data to stdout for protocol verification */ - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); - test_setopt(curl, CURLOPT_USERAGENT, "test567"); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, URL); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); + easy_setopt(curl, CURLOPT_USERAGENT, "test567"); custom_headers = curl_slist_append(custom_headers, "Test-Number: 567"); - test_setopt(curl, CURLOPT_RTSPHEADER, custom_headers); + easy_setopt(curl, CURLOPT_RTSPHEADER, custom_headers); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib568.c b/tests/libtest/lib568.c index 8f650e53ee56..654de68461a3 100644 --- a/tests/libtest/lib568.c +++ b/tests/libtest/lib568.c @@ -23,8 +23,6 @@ ***************************************************************************/ #include "first.h" -#include "testutil.h" - /* * Test the Client->Server ANNOUNCE functionality (PUT style) */ @@ -52,17 +50,17 @@ static CURLcode test_lib568(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -85,19 +83,19 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE); - test_setopt(curl, CURLOPT_READDATA, sdpf); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_READDATA, sdpf); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Do the ANNOUNCE */ result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_UPLOAD, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 0L); curlx_fclose(sdpf); sdpf = NULL; @@ -107,11 +105,11 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -123,7 +121,7 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -133,17 +131,17 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSPHEADER, custom_headers); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE); - test_setopt(curl, CURLOPT_POSTFIELDS, + easy_setopt(curl, CURLOPT_RTSPHEADER, custom_headers); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_ANNOUNCE); + easy_setopt(curl, CURLOPT_POSTFIELDS, "postyfield=postystuff&project=curl\n"); result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_POSTFIELDS, NULL); - test_setopt(curl, CURLOPT_RTSPHEADER, NULL); + easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + easy_setopt(curl, CURLOPT_RTSPHEADER, NULL); curl_slist_free_all(custom_headers); custom_headers = NULL; @@ -153,11 +151,11 @@ static CURLcode test_lib568(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib569.c b/tests/libtest/lib569.c index e29b7d71ee15..8f79eeb71b3b 100644 --- a/tests/libtest/lib569.c +++ b/tests/libtest/lib569.c @@ -23,8 +23,6 @@ ***************************************************************************/ #include "first.h" -#include "testutil.h" - /* * Test Session ID capture */ @@ -56,13 +54,13 @@ static CURLcode test_lib569(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); result = curl_easy_perform(curl); if(result != CURLE_BAD_FUNCTION_ARGUMENT) { curl_mfprintf(stderr, "This should have failed. " @@ -79,12 +77,12 @@ static CURLcode test_lib569(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); - test_setopt(curl, CURLOPT_RTSP_TRANSPORT, + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "Fake/NotReal/JustATest;foo=baz"); result = curl_easy_perform(curl); if(result) @@ -99,17 +97,17 @@ static CURLcode test_lib569(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_TEARDOWN); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_TEARDOWN); result = curl_easy_perform(curl); if(result) goto test_cleanup; /* Clear for the next go-round */ - test_setopt(curl, CURLOPT_RTSP_SESSION_ID, NULL); + easy_setopt(curl, CURLOPT_RTSP_SESSION_ID, NULL); } test_cleanup: diff --git a/tests/libtest/lib570.c b/tests/libtest/lib570.c index 5f2ab2d6eff1..998dd68cfa4e 100644 --- a/tests/libtest/lib570.c +++ b/tests/libtest/lib570.c @@ -23,8 +23,6 @@ ***************************************************************************/ #include "first.h" -#include "testutil.h" - static CURLcode test_lib570(const char *URL) { CURLcode result; @@ -44,20 +42,20 @@ static CURLcode test_lib570(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -68,17 +66,17 @@ static CURLcode test_lib570(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_CLIENT_CSEQ, 999L); - test_setopt(curl, CURLOPT_RTSP_TRANSPORT, + easy_setopt(curl, CURLOPT_RTSP_CLIENT_CSEQ, 999L); + easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RAW/RAW/UDP;unicast;client_port=3056-3057"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -86,14 +84,14 @@ static CURLcode test_lib570(const char *URL) if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; diff --git a/tests/libtest/lib571.c b/tests/libtest/lib571.c index a5e479d37552..37b93e539078 100644 --- a/tests/libtest/lib571.c +++ b/tests/libtest/lib571.c @@ -33,8 +33,6 @@ #include #endif -#include "testutil.h" - #define RTP_PKT_CHANNEL(p) ((int)((unsigned char)((p)[1]))) #define RTP_PKT_LENGTH(p) ((((int)((unsigned char)((p)[2]))) << 8) | \ @@ -67,7 +65,7 @@ static size_t rtp_write(char *data, size_t size, size_t nmemb, void *stream) data += 4; for(i = 0; i < message_size; i += RTP_DATA_SIZE) { if(message_size - i > RTP_DATA_SIZE) { - if(memcmp(RTP_DATA, data + i, RTP_DATA_SIZE) != 0) { + if(memcmp(RTP_DATA, data + i, RTP_DATA_SIZE)) { curl_mprintf("RTP PAYLOAD CORRUPTED [%s]\n", data + i); #if 0 return failure; @@ -75,7 +73,7 @@ static size_t rtp_write(char *data, size_t size, size_t nmemb, void *stream) } } else { - if(memcmp(RTP_DATA, data + i, message_size - i) != 0) { + if(memcmp(RTP_DATA, data + i, message_size - i)) { curl_mprintf("RTP PAYLOAD END CORRUPTED (%d), [%s]\n", message_size - i, data + i); #if 0 @@ -117,24 +115,24 @@ static CURLcode test_lib571(const char *URL) curl_global_cleanup(); return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_INTERLEAVEFUNCTION, rtp_write); - test_setopt(curl, CURLOPT_TIMEOUT, 30L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_WRITEDATA, protofile); + easy_setopt(curl, CURLOPT_INTERLEAVEFUNCTION, rtp_write); + easy_setopt(curl, CURLOPT_TIMEOUT, 30L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_WRITEDATA, protofile); - test_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RTP/AVP/TCP;interleaved=0-1"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "RTP/AVP/TCP;interleaved=0-1"); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); result = curl_easy_perform(curl); if(result) @@ -146,25 +144,25 @@ static CURLcode test_lib571(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); result = curl_easy_perform(curl); if(result) goto test_cleanup; - /* The DESCRIBE request will try to consume data after the Content */ + /* The DESCRIBE request tries to consume data after the Content */ stream_uri = tutil_suburl(URL, request++); if(!stream_uri) { result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_DESCRIBE); result = curl_easy_perform(curl); if(result) @@ -175,10 +173,10 @@ static CURLcode test_lib571(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_PLAY); result = curl_easy_perform(curl); if(result) @@ -189,7 +187,7 @@ static CURLcode test_lib571(const char *URL) /* Use Receive to get the rest of the data */ while(!result && rtp_packet_count < 19) { curl_mfprintf(stderr, "LOOPY LOOP!\n"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_RECEIVE); result = curl_easy_perform(curl); } diff --git a/tests/libtest/lib572.c b/tests/libtest/lib572.c index 86256c020bbb..1ed292761f2f 100644 --- a/tests/libtest/lib572.c +++ b/tests/libtest/lib572.c @@ -23,8 +23,6 @@ ***************************************************************************/ #include "first.h" -#include "testutil.h" - /* * Test GET_PARAMETER: PUT, HEARTBEAT, and POST */ @@ -52,11 +50,11 @@ static CURLcode test_lib572(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_HEADERDATA, stdout); - test_setopt(curl, CURLOPT_WRITEDATA, stdout); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HEADERDATA, stdout); + easy_setopt(curl, CURLOPT_WRITEDATA, stdout); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* SETUP */ stream_uri = tutil_suburl(URL, request++); @@ -64,12 +62,12 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_TRANSPORT, "Planes/Trains/Automobiles"); - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); + easy_setopt(curl, CURLOPT_RTSP_TRANSPORT, "Planes/Trains/Automobiles"); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_SETUP); result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -79,7 +77,7 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -103,17 +101,17 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_GET_PARAMETER); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_GET_PARAMETER); - test_setopt(curl, CURLOPT_READDATA, paramsf); - test_setopt(curl, CURLOPT_UPLOAD, 1L); - test_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + easy_setopt(curl, CURLOPT_READDATA, paramsf); + easy_setopt(curl, CURLOPT_UPLOAD, 1L); + easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_UPLOAD, 0L); + easy_setopt(curl, CURLOPT_UPLOAD, 0L); curlx_fclose(paramsf); paramsf = NULL; @@ -123,7 +121,7 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; @@ -138,18 +136,18 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_GET_PARAMETER); - test_setopt(curl, CURLOPT_POSTFIELDS, "packets_received\njitter\n"); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_GET_PARAMETER); + easy_setopt(curl, CURLOPT_POSTFIELDS, "packets_received\njitter\n"); result = curl_easy_perform(curl); if(result) goto test_cleanup; - test_setopt(curl, CURLOPT_POSTFIELDS, NULL); + easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); /* Make sure we can do a normal request now */ stream_uri = tutil_suburl(URL, request++); @@ -157,11 +155,11 @@ static CURLcode test_lib572(const char *URL) result = TEST_ERR_MAJOR_BAD; goto test_cleanup; } - test_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); + easy_setopt(curl, CURLOPT_RTSP_STREAM_URI, stream_uri); curl_free(stream_uri); stream_uri = NULL; - test_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); + easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib574.c b/tests/libtest/lib574.c index 6f1f061ee2b2..6b5e415fa74b 100644 --- a/tests/libtest/lib574.c +++ b/tests/libtest/lib574.c @@ -48,19 +48,19 @@ static CURLcode test_lib574(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); - test_setopt(curl, CURLOPT_FNMATCH_FUNCTION, new_fnmatch); - test_setopt(curl, CURLOPT_TIMEOUT_MS, (long)TEST_HANG_TIMEOUT); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); + easy_setopt(curl, CURLOPT_FNMATCH_FUNCTION, new_fnmatch); + easy_setopt(curl, CURLOPT_TIMEOUT_MS, (long)TEST_HANG_TIMEOUT); result = curl_easy_perform(curl); if(result) { - curl_mfprintf(stderr, "curl_easy_perform() failed %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() failed %d\n", (int)result); goto test_cleanup; } result = curl_easy_perform(curl); if(result) { - curl_mfprintf(stderr, "curl_easy_perform() failed %d\n", result); + curl_mfprintf(stderr, "curl_easy_perform() failed %d\n", (int)result); goto test_cleanup; } diff --git a/tests/libtest/lib576.c b/tests/libtest/lib576.c index a50d34c02503..889969d5b852 100644 --- a/tests/libtest/lib576.c +++ b/tests/libtest/lib576.c @@ -73,7 +73,7 @@ static long chunk_bgn(const void *f, void *ptr, int remains) "-------------------------------------------" "------------------\n"); } - if(strcmp(finfo->filename, "someothertext.txt") == 0) { + if(!strcmp(finfo->filename, "someothertext.txt")) { curl_mprintf("# THIS CONTENT WAS SKIPPED IN CHUNK_BGN CALLBACK #\n"); return CURL_CHUNK_BGN_FUNC_SKIP; } @@ -106,11 +106,11 @@ static CURLcode test_lib576(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); - test_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn); - test_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, chunk_end); - test_setopt(curl, CURLOPT_CHUNK_DATA, &chunk_data); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_WILDCARDMATCH, 1L); + easy_setopt(curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn); + easy_setopt(curl, CURLOPT_CHUNK_END_FUNCTION, chunk_end); + easy_setopt(curl, CURLOPT_CHUNK_DATA, &chunk_data); result = curl_easy_perform(curl); diff --git a/tests/libtest/lib578.c b/tests/libtest/lib578.c index 75b3e8af36fa..434178552467 100644 --- a/tests/libtest/lib578.c +++ b/tests/libtest/lib578.c @@ -66,26 +66,26 @@ static CURLcode test_lib578(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* Set the expected POST size */ - test_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)data_size); - test_setopt(curl, CURLOPT_POSTFIELDS, t578_testdata); + easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)data_size); + easy_setopt(curl, CURLOPT_POSTFIELDS, t578_testdata); /* we want to use our own progress function */ - test_setopt(curl, CURLOPT_NOPROGRESS, 0L); - test_setopt(curl, CURLOPT_PROGRESSFUNCTION, t578_progress_callback); + easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, t578_progress_callback); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib579.c b/tests/libtest/lib579.c index 35ccd8485579..7ad133188bc5 100644 --- a/tests/libtest/lib579.c +++ b/tests/libtest/lib579.c @@ -127,34 +127,34 @@ static CURLcode test_lib579(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Now specify we want to POST data */ - test_setopt(curl, CURLOPT_POST, 1L); + easy_setopt(curl, CURLOPT_POST, 1L); /* we want to use our own read function */ - test_setopt(curl, CURLOPT_READFUNCTION, t579_read_cb); + easy_setopt(curl, CURLOPT_READFUNCTION, t579_read_cb); /* pointer to pass to our read function */ - test_setopt(curl, CURLOPT_READDATA, &pooh); + easy_setopt(curl, CURLOPT_READDATA, &pooh); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* enforce chunked transfer by setting the header */ - test_setopt(curl, CURLOPT_HTTPHEADER, slist); + easy_setopt(curl, CURLOPT_HTTPHEADER, slist); - test_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); - test_setopt(curl, CURLOPT_USERPWD, "foo:bar"); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + easy_setopt(curl, CURLOPT_USERPWD, "foo:bar"); /* we want to use our own progress function */ - test_setopt(curl, CURLOPT_NOPROGRESS, 0L); - test_setopt(curl, CURLOPT_PROGRESSFUNCTION, t579_progress_callback); + easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, t579_progress_callback); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); progress_final_report(); diff --git a/tests/libtest/lib582.c b/tests/libtest/lib582.c index 3a4e278e383f..4b3f042852ec 100644 --- a/tests/libtest/lib582.c +++ b/tests/libtest/lib582.c @@ -150,8 +150,8 @@ static int t582_checkForCompletion(CURLM *multi, int *success) *success = 0; } else { - curl_mfprintf(stderr, "Got an unexpected message from curl: %i\n", - message->msg); + curl_mfprintf(stderr, "Got an unexpected message from curl: %d\n", + (int)message->msg); result = 1; *success = 0; } @@ -194,7 +194,7 @@ static void notifyCurl(CURLM *multi, curl_socket_t s, int evBitmask, CURLMcode mresult = curl_multi_socket_action(multi, s, evBitmask, &numhandles); if(mresult != CURLM_OK) { - curl_mfprintf(stderr, "curl error on %s (%i) %s\n", + curl_mfprintf(stderr, "curl error on %s (%d) %s\n", info, mresult, curl_multi_strerror(mresult)); } } diff --git a/tests/libtest/lib583.c b/tests/libtest/lib583.c index 984764eb12db..8f366003f483 100644 --- a/tests/libtest/lib583.c +++ b/tests/libtest/lib583.c @@ -45,7 +45,7 @@ static CURLcode test_lib583(const char *URL) easy_init(curl); easy_setopt(curl, CURLOPT_USERPWD, libtest_arg2); - easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, test_argv[3]); + easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, test_argv[3]); easy_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, test_argv[4]); easy_setopt(curl, CURLOPT_UPLOAD, 1L); diff --git a/tests/libtest/lib586.c b/tests/libtest/lib586.c index 8609a1d16907..29eab1844362 100644 --- a/tests/libtest/lib586.c +++ b/tests/libtest/lib586.c @@ -60,7 +60,7 @@ static void t586_test_lock(CURL *curl, curl_lock_data data, what = "ssl_session"; break; default: - curl_mfprintf(stderr, "lock: no such data: %d\n", data); + curl_mfprintf(stderr, "lock: no such data: %d\n", (int)data); return; } curl_mprintf("lock: %-6s [%s]: %d\n", what, user->text, user->counter); @@ -87,7 +87,7 @@ static void t586_test_unlock(CURL *curl, curl_lock_data data, void *useptr) what = "ssl_session"; break; default: - curl_mfprintf(stderr, "unlock: no such data: %d\n", data); + curl_mfprintf(stderr, "unlock: no such data: %d\n", (int)data); return; } curl_mprintf("unlock: %-6s [%s]: %d\n", what, user->text, user->counter); @@ -118,7 +118,7 @@ static void *t586_test_fire(void *ptr) if(result != CURLE_OK) { int i = 0; curl_mfprintf(stderr, "perform URL '%s' repeat %d failed, curlcode %d\n", - tdata->url, i, result); + tdata->url, i, (int)result); } curl_mprintf("CLEANUP\n"); @@ -203,9 +203,9 @@ static CURLcode test_lib586(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); curl_mprintf("CURLOPT_SHARE\n"); - test_setopt(curl, CURLOPT_SHARE, share); + easy_setopt(curl, CURLOPT_SHARE, share); curl_mprintf("PERFORM\n"); result = curl_easy_perform(curl); @@ -231,7 +231,8 @@ static CURLcode test_lib586(const char *URL) curl_mprintf("SHARE_CLEANUP\n"); scode = curl_share_cleanup(share); if(scode != CURLSHE_OK) - curl_mfprintf(stderr, "curl_share_cleanup failed, code errno %d\n", scode); + curl_mfprintf(stderr, "curl_share_cleanup failed, code errno %d\n", + (int)scode); curl_mprintf("GLOBAL_CLEANUP\n"); curl_global_cleanup(); diff --git a/tests/libtest/lib589.c b/tests/libtest/lib589.c index cf8565237829..1f4b6cfbe124 100644 --- a/tests/libtest/lib589.c +++ b/tests/libtest/lib589.c @@ -43,9 +43,9 @@ static CURLcode test_lib589(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ - test_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* show verbose for debug */ + easy_setopt(curl, CURLOPT_HEADER, 1L); /* include header */ if(testnum == 584) { mime = curl_mime_init(curl); @@ -53,14 +53,14 @@ static CURLcode test_lib589(const char *URL) if(mime && part) { curl_mime_name(part, "fake"); curl_mime_data(part, "party", 5); - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); result = curl_easy_perform(curl); } if(result) goto test_cleanup; } - test_setopt(curl, CURLOPT_MIMEPOST, NULL); + easy_setopt(curl, CURLOPT_MIMEPOST, NULL); /* Now, we should be making a zero byte POST request */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib590.c b/tests/libtest/lib590.c index 1cea00b6e24a..3a83e62701ab 100644 --- a/tests/libtest/lib590.c +++ b/tests/libtest/lib590.c @@ -55,16 +55,16 @@ static CURLcode test_lib590(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_PROXYAUTH, + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM); - test_setopt(curl, CURLOPT_PROXY, libtest_arg2); /* set in first.c */ + easy_setopt(curl, CURLOPT_PROXY, libtest_arg2); /* set in first.c */ /* set the name + password twice to test that the API is fine with it */ - test_setopt(curl, CURLOPT_PROXYUSERNAME, "me"); - test_setopt(curl, CURLOPT_PROXYPASSWORD, "password"); - test_setopt(curl, CURLOPT_PROXYUSERPWD, "me:password"); + easy_setopt(curl, CURLOPT_PROXYUSERNAME, "me"); + easy_setopt(curl, CURLOPT_PROXYPASSWORD, "password"); + easy_setopt(curl, CURLOPT_PROXYUSERPWD, "me:password"); result = curl_easy_perform(curl); if(result) diff --git a/tests/libtest/lib598.c b/tests/libtest/lib598.c index dc89a52f004a..c6025c578b5e 100644 --- a/tests/libtest/lib598.c +++ b/tests/libtest/lib598.c @@ -40,12 +40,12 @@ static CURLcode test_lib598(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_REFERER, "http://example.com/the-moo"); - test_setopt(curl, CURLOPT_USERAGENT, "the-moo agent next generation"); - test_setopt(curl, CURLOPT_COOKIE, "name=moo"); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_REFERER, "http://example.com/the-moo"); + easy_setopt(curl, CURLOPT_USERAGENT, "the-moo agent next generation"); + easy_setopt(curl, CURLOPT_COOKIE, "name=moo"); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); if(result) { @@ -55,9 +55,9 @@ static CURLcode test_lib598(const char *URL) curl_easy_reset(curl); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); if(result) diff --git a/tests/libtest/lib599.c b/tests/libtest/lib599.c index 079fc578dfda..ba8ebca97bbf 100644 --- a/tests/libtest/lib599.c +++ b/tests/libtest/lib599.c @@ -58,28 +58,28 @@ static CURLcode test_lib599(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* we want to use our own progress function */ - test_setopt(curl, CURLOPT_NOPROGRESS, 0L); - test_setopt(curl, CURLOPT_PROGRESSFUNCTION, t599_progress_callback); + easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, t599_progress_callback); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* follow redirects */ - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); if(!result) { FILE *moo; result = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, - &content_length); + &content_length); moo = curlx_fopen(libtest_arg2, "wb"); if(moo) { curl_mfprintf(moo, "CL %.0f\n", content_length); diff --git a/tests/libtest/lib643.c b/tests/libtest/lib643.c index 63df7e0dc7e0..eaf75e8fe153 100644 --- a/tests/libtest/lib643.c +++ b/tests/libtest/lib643.c @@ -46,12 +46,12 @@ static size_t t643_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) } if(!eof) { - *ptr = *pooh->readptr; /* copy one single byte */ - pooh->readptr++; /* advance pointer */ - return 1; /* we return 1 byte at a time! */ + *ptr = *pooh->readptr; /* copy one single byte */ + pooh->readptr++; /* advance pointer */ + return 1; /* we return 1 byte at a time! */ } - return 0; /* no more data left to deliver */ + return 0; /* no more data left to deliver */ } static CURLcode t643_test_once(const char *URL, bool oldstyle) @@ -69,7 +69,7 @@ static CURLcode t643_test_once(const char *URL, bool oldstyle) pooh.readptr = testdata; if(testnum == 643) - datasize = (curl_off_t)strlen(testdata); + datasize = (curl_off_t)(sizeof(testdata) - 1); pooh.sizeleft = datasize; curl = curl_easy_init(); @@ -123,7 +123,7 @@ static CURLcode t643_test_once(const char *URL, bool oldstyle) pooh2.readptr = testdata; if(testnum == 643) - datasize = (curl_off_t)strlen(testdata); + datasize = (curl_off_t)(sizeof(testdata) - 1); pooh2.sizeleft = datasize; part = curl_mime_addpart(mime); @@ -194,18 +194,18 @@ static CURLcode t643_test_once(const char *URL, bool oldstyle) curl_mprintf("curl_mime_xxx(5) = %s\n", curl_easy_strerror(result)); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* send a multi-part mimepost */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib650.c b/tests/libtest/lib650.c index 0d3c41998693..b0766eee1d0a 100644 --- a/tests/libtest/lib650.c +++ b/tests/libtest/lib650.c @@ -48,7 +48,6 @@ static CURLcode test_lib650(const char *URL) struct curl_forms formarray[3]; size_t formlength = 0; char flbuf[32]; - long contentlength = 0; static const char testname[] = "fieldname"; static char testdata[] = "this is what we post to the silly web server"; @@ -79,29 +78,27 @@ static CURLcode test_lib650(const char *URL) CURLFORM_CONTENTHEADER, headers, CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(1) = %d\n", formrc); + curl_mprintf("curl_formadd(1) = %d\n", (int)formrc); goto test_cleanup; } - contentlength = (long)(strlen(testdata) - 1); - /* Use a form array for the non-copy test. */ formarray[0].option = CURLFORM_PTRCONTENTS; formarray[0].value = testdata; formarray[1].option = CURLFORM_CONTENTSLENGTH; - formarray[1].value = (char *)(size_t)contentlength; + formarray[1].value = (char *)(strlen(testdata) - 1); formarray[2].option = CURLFORM_END; formarray[2].value = NULL; formrc = curl_formadd(&formpost, &lastptr, CURLFORM_PTRNAME, testname, - CURLFORM_NAMELENGTH, strlen(testname) - 1, + CURLFORM_NAMELENGTH, (long)(sizeof(testname) - 2), CURLFORM_ARRAY, formarray, CURLFORM_FILENAME, "remotefile.txt", CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(2) = %d\n", formrc); + curl_mprintf("curl_formadd(2) = %d\n", (int)formrc); goto test_cleanup; } @@ -121,7 +118,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(3) = %d\n", formrc); + curl_mprintf("curl_formadd(3) = %d\n", (int)formrc); goto test_cleanup; } @@ -132,7 +129,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_FILECONTENT, libtest_arg2, CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(4) = %d\n", formrc); + curl_mprintf("curl_formadd(4) = %d\n", (int)formrc); goto test_cleanup; } @@ -152,7 +149,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(5) = %d\n", formrc); + curl_mprintf("curl_formadd(5) = %d\n", (int)formrc); goto test_cleanup; } @@ -164,7 +161,7 @@ static CURLcode test_lib650(const char *URL) CURLFORM_END); if(formrc) { - curl_mprintf("curl_formadd(6) = %d\n", formrc); + curl_mprintf("curl_formadd(6) = %d\n", (int)formrc); goto test_cleanup; } @@ -175,21 +172,21 @@ static CURLcode test_lib650(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* send a multi-part formpost */ - test_setopt(curl, CURLOPT_HTTPPOST, formpost); + easy_setopt(curl, CURLOPT_HTTPPOST, formpost); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - test_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); + easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_301); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib651.c b/tests/libtest/lib651.c index 784e17f02758..1c8fca2fd1ec 100644 --- a/tests/libtest/lib651.c +++ b/tests/libtest/lib651.c @@ -53,7 +53,7 @@ static CURLcode test_lib651(const char *URL) CURLFORM_COPYCONTENTS, testbuf, CURLFORM_END); if(formrc) - curl_mprintf("curl_formadd(1) = %d\n", formrc); + curl_mprintf("curl_formadd(1) = %d\n", (int)formrc); curl = curl_easy_init(); if(!curl) { @@ -64,18 +64,18 @@ static CURLcode test_lib651(const char *URL) } /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* send a multi-part formpost */ - test_setopt(curl, CURLOPT_HTTPPOST, formpost); + easy_setopt(curl, CURLOPT_HTTPPOST, formpost); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib652.c b/tests/libtest/lib652.c index d606fa069419..bf225348ed8c 100644 --- a/tests/libtest/lib652.c +++ b/tests/libtest/lib652.c @@ -94,24 +94,24 @@ static CURLcode test_lib652(const char *URL) } /* First set the URL that is about to receive our mime mail. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Set sender. */ - test_setopt(curl, CURLOPT_MAIL_FROM, "somebody@example.com"); + easy_setopt(curl, CURLOPT_MAIL_FROM, "somebody@example.com"); /* Set recipients. */ - test_setopt(curl, CURLOPT_MAIL_RCPT, recipients); + easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); /* send a multi-part mail */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Shorten upload buffer. */ - test_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 16411L); + easy_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 16411L); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib654.c b/tests/libtest/lib654.c index 472983559934..0494d580abfc 100644 --- a/tests/libtest/lib654.c +++ b/tests/libtest/lib654.c @@ -49,12 +49,12 @@ static size_t t654_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) pooh->sizeleft--; if(!eof) { - *ptr = *pooh->readptr; /* copy one single byte */ - pooh->readptr++; /* advance pointer */ - return 1; /* we return 1 byte at a time! */ + *ptr = *pooh->readptr; /* copy one single byte */ + pooh->readptr++; /* advance pointer */ + return 1; /* we return 1 byte at a time! */ } - return 0; /* no more data left to deliver */ + return 0; /* no more data left to deliver */ } static CURLcode test_lib654(const char *URL) @@ -82,17 +82,17 @@ static CURLcode test_lib654(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Prepare the callback structure. */ pooh.readptr = testdata; - pooh.sizeleft = (curl_off_t)strlen(testdata); + pooh.sizeleft = (curl_off_t)(sizeof(testdata) - 1); pooh.freecount = 0; /* Build the mime tree. */ @@ -111,7 +111,7 @@ static CURLcode test_lib654(const char *URL) free_callback, &pooh); /* Bind mime data to its easy handle. */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Duplicate the handle. */ curl2 = curl_easy_duphandle(curl); @@ -143,7 +143,7 @@ static CURLcode test_lib654(const char *URL) /* Free the duplicated handle: it should call free_callback again. If the mime copy was bad or not automatically released, valgrind - will signal it. */ + signals it. */ curl_easy_cleanup(curl2); curl2 = NULL; /* Already cleaned up. */ diff --git a/tests/libtest/lib655.c b/tests/libtest/lib655.c index e74d2de17d6d..3dd70dfa7351 100644 --- a/tests/libtest/lib655.c +++ b/tests/libtest/lib655.c @@ -78,32 +78,32 @@ static CURLcode test_lib655(const char *URL) } /* Set the URL that is about to receive our first request. */ - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_RESOLVER_START_DATA, TEST_DATA_STRING); - test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_fail); + easy_setopt(curl, CURLOPT_RESOLVER_START_DATA, TEST_DATA_STRING); + easy_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_fail); /* this should fail */ result = curl_easy_perform(curl); if(result != CURLE_ABORTED_BY_CALLBACK) { curl_mfprintf(stderr, "curl_easy_perform should have returned " "CURLE_ABORTED_BY_CALLBACK but instead returned error %d\n", - result); + (int)result); if(result == CURLE_OK) result = TEST_ERR_FAILURE; goto test_cleanup; } /* Set the URL that receives our second request. */ - test_setopt(curl, CURLOPT_URL, libtest_arg2); - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); - test_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, libtest_arg2); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_pass); + easy_setopt(curl, CURLOPT_RESOLVER_START_FUNCTION, resolver_alloc_cb_pass); /* this should succeed */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib658.c b/tests/libtest/lib658.c index 3d9b4575d460..8ed465715fea 100644 --- a/tests/libtest/lib658.c +++ b/tests/libtest/lib658.c @@ -61,8 +61,8 @@ static CURLcode test_lib658(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed " - "with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + "with code %d (%s)\n", __FILE__, __LINE__, + (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib659.c b/tests/libtest/lib659.c index d1a78e206dbf..c2fb30d4ea24 100644 --- a/tests/libtest/lib659.c +++ b/tests/libtest/lib659.c @@ -60,7 +60,7 @@ static CURLcode test_lib659(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed " "with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib661.c b/tests/libtest/lib661.c index 12cb4b7250fd..80d46e8738bf 100644 --- a/tests/libtest/lib661.c +++ b/tests/libtest/lib661.c @@ -45,17 +45,17 @@ static CURLcode test_lib661(const char *URL) /* test: CURLFTPMETHOD_SINGLECWD with absolute path should skip CWD to entry path */ newURL = curl_maprintf("%s/folderA/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); + easy_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; curl_free(newURL); newURL = curl_maprintf("%s/folderB/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_URL, newURL); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; @@ -72,10 +72,10 @@ static CURLcode test_lib661(const char *URL) curl_free(newURL); newURL = curl_maprintf("%s/folderA/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_IGNORE_CONTENT_LENGTH, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; @@ -83,16 +83,16 @@ static CURLcode test_lib661(const char *URL) /* curve ball: CWD /folderB before reusing connection with _NOCWD */ curl_free(newURL); newURL = curl_maprintf("%s/folderB/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); + easy_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; curl_free(newURL); newURL = curl_maprintf("%s/folderA/661", URL); - test_setopt(curl, CURLOPT_URL, newURL); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_URL, newURL); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); result = curl_easy_perform(curl); if(result != CURLE_REMOTE_FILE_NOT_FOUND) goto test_cleanup; @@ -114,11 +114,11 @@ static CURLcode test_lib661(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); - test_setopt(curl, CURLOPT_QUOTE, slist); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_QUOTE, slist); result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -133,11 +133,11 @@ static CURLcode test_lib661(const char *URL) goto test_cleanup; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); - test_setopt(curl, CURLOPT_QUOTE, slist); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD); + easy_setopt(curl, CURLOPT_QUOTE, slist); result = curl_easy_perform(curl); if(result) goto test_cleanup; @@ -146,17 +146,17 @@ static CURLcode test_lib661(const char *URL) not emit CWD for second FTP access when not needed + bonus: see if path buffering survives curl_easy_reset() */ curl_easy_reset(curl); - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_NOBODY, 1L); - test_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); - test_setopt(curl, CURLOPT_QUOTE, slist); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_NOBODY, 1L); + easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD); + easy_setopt(curl, CURLOPT_QUOTE, slist); result = curl_easy_perform(curl); test_cleanup: if(result) - curl_mfprintf(stderr, "test encountered error %d\n", result); + curl_mfprintf(stderr, "test encountered error %d\n", (int)result); curl_slist_free_all(slist); curl_free(newURL); curl_easy_cleanup(curl); diff --git a/tests/libtest/lib666.c b/tests/libtest/lib666.c index 55dd33ab35a5..43ea1eb88e80 100644 --- a/tests/libtest/lib666.c +++ b/tests/libtest/lib666.c @@ -89,21 +89,21 @@ static CURLcode test_lib666(const char *URL) } /* First set the URL that is about to receive our mime mail. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* Post form */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Shorten upload buffer. */ - test_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 16411L); + easy_setopt(curl, CURLOPT_UPLOAD_BUFFERSIZE, 16411L); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); - /* Perform the request, result will get the return code */ + /* Perform the request, result gets the return code */ result = curl_easy_perform(curl); test_cleanup: diff --git a/tests/libtest/lib667.c b/tests/libtest/lib667.c index c0b383d0ac21..28a9cde6d2b3 100644 --- a/tests/libtest/lib667.c +++ b/tests/libtest/lib667.c @@ -41,12 +41,12 @@ static size_t t667_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) pooh->sizeleft--; if(!eof) { - *ptr = *pooh->readptr; /* copy one single byte */ - pooh->readptr++; /* advance pointer */ - return 1; /* we return 1 byte at a time! */ + *ptr = *pooh->readptr; /* copy one single byte */ + pooh->readptr++; /* advance pointer */ + return 1; /* we return 1 byte at a time! */ } - return 0; /* no more data left to deliver */ + return 0; /* no more data left to deliver */ } static CURLcode test_lib667(const char *URL) @@ -72,17 +72,17 @@ static CURLcode test_lib667(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Prepare the callback structure. */ pooh.readptr = testdata; - pooh.sizeleft = (curl_off_t)strlen(testdata); + pooh.sizeleft = (curl_off_t)(sizeof(testdata) - 1); /* Build the mime tree. */ mime = curl_mime_init(curl); @@ -93,7 +93,7 @@ static CURLcode test_lib667(const char *URL) curl_mime_data_cb(part, (curl_off_t)-1, t667_read_cb, NULL, NULL, &pooh); /* Bind mime data to its easy handle. */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Send data. */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib668.c b/tests/libtest/lib668.c index ae0f524e4799..33cf25e54982 100644 --- a/tests/libtest/lib668.c +++ b/tests/libtest/lib668.c @@ -25,13 +25,13 @@ struct t668_WriteThis { const char *readptr; - curl_off_t sizeleft; + size_t sizeleft; }; static size_t t668_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) { struct t668_WriteThis *pooh = (struct t668_WriteThis *)userp; - size_t len = strlen(pooh->readptr); + size_t len = pooh->sizeleft; (void)size; /* Always 1 */ @@ -40,6 +40,7 @@ static size_t t668_read_cb(char *ptr, size_t size, size_t nmemb, void *userp) if(len) { memcpy(ptr, pooh->readptr, len); pooh->readptr += len; + pooh->sizeleft -= len; } return len; } @@ -66,17 +67,17 @@ static CURLcode test_lib668(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_HEADER, 1L); /* Prepare the callback structures. */ pooh1.readptr = testdata; - pooh1.sizeleft = (curl_off_t)strlen(testdata); + pooh1.sizeleft = sizeof(testdata) - 1; pooh2 = pooh1; /* Build the mime tree. */ @@ -84,14 +85,13 @@ static CURLcode test_lib668(const char *URL) part = curl_mime_addpart(mime); curl_mime_name(part, "field1"); /* Early end of data detection can be done because the data size is known. */ - curl_mime_data_cb(part, (curl_off_t)strlen(testdata), + curl_mime_data_cb(part, (curl_off_t)pooh1.sizeleft, t668_read_cb, NULL, NULL, &pooh1); part = curl_mime_addpart(mime); curl_mime_name(part, "field2"); /* Using an undefined length forces chunked transfer and disables early end of data detection for this part. */ - curl_mime_data_cb(part, (curl_off_t)-1, - t668_read_cb, NULL, NULL, &pooh2); + curl_mime_data_cb(part, (curl_off_t)-1, t668_read_cb, NULL, NULL, &pooh2); part = curl_mime_addpart(mime); curl_mime_name(part, "field3"); /* Regular file part sources early end of data can be detected because @@ -99,7 +99,7 @@ static CURLcode test_lib668(const char *URL) curl_mime_filedata(part, libtest_arg2); /* Bind mime data to its easy handle. */ - test_setopt(curl, CURLOPT_MIMEPOST, mime); + easy_setopt(curl, CURLOPT_MIMEPOST, mime); /* Send data. */ result = curl_easy_perform(curl); diff --git a/tests/libtest/lib670.c b/tests/libtest/lib670.c index 60e4f7ef25c4..2daeb06576a7 100644 --- a/tests/libtest/lib670.c +++ b/tests/libtest/lib670.c @@ -105,13 +105,13 @@ static CURLcode test_lib670(const char *URL) pooh.curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(pooh.curl, CURLOPT_URL, URL); + easy_setopt(pooh.curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(pooh.curl, CURLOPT_VERBOSE, 1L); + easy_setopt(pooh.curl, CURLOPT_VERBOSE, 1L); /* include headers in the output */ - test_setopt(pooh.curl, CURLOPT_HEADER, 1L); + easy_setopt(pooh.curl, CURLOPT_HEADER, 1L); if(testnum == 670 || testnum == 671) { curl_mimepart *part; @@ -122,7 +122,7 @@ static CURLcode test_lib670(const char *URL) if(result != CURLE_OK) { curl_mfprintf(stderr, "Something went wrong when building the " - "mime structure: %d\n", result); + "mime structure: %d\n", (int)result); goto test_cleanup; } @@ -131,7 +131,7 @@ static CURLcode test_lib670(const char *URL) /* Bind mime data to its easy handle. */ if(result == CURLE_OK) - test_setopt(pooh.curl, CURLOPT_MIMEPOST, mime); + easy_setopt(pooh.curl, CURLOPT_MIMEPOST, mime); } else { struct curl_httppost *lastptr = NULL; @@ -140,18 +140,18 @@ static CURLcode test_lib670(const char *URL) formrc = curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, testname, CURLFORM_STREAM, &pooh, - CURLFORM_CONTENTLEN, (curl_off_t) 2, + CURLFORM_CONTENTLEN, (curl_off_t)2, CURLFORM_END); if(formrc) { - curl_mfprintf(stderr, "curl_formadd() = %d\n", formrc); + curl_mfprintf(stderr, "curl_formadd() = %d\n", (int)formrc); goto test_cleanup; } /* We want to use our own read function. */ - test_setopt(pooh.curl, CURLOPT_READFUNCTION, t670_read_cb); + easy_setopt(pooh.curl, CURLOPT_READFUNCTION, t670_read_cb); /* Send a multi-part formpost. */ - test_setopt(pooh.curl, CURLOPT_HTTPPOST, formpost); + easy_setopt(pooh.curl, CURLOPT_HTTPPOST, formpost); } if(testnum == 670 || testnum == 672) { @@ -223,9 +223,9 @@ static CURLcode test_lib670(const char *URL) } else { /* Use the easy interface. */ - test_setopt(pooh.curl, CURLOPT_XFERINFODATA, &pooh); - test_setopt(pooh.curl, CURLOPT_XFERINFOFUNCTION, t670_xferinfo); - test_setopt(pooh.curl, CURLOPT_NOPROGRESS, 0L); + easy_setopt(pooh.curl, CURLOPT_XFERINFODATA, &pooh); + easy_setopt(pooh.curl, CURLOPT_XFERINFOFUNCTION, t670_xferinfo); + easy_setopt(pooh.curl, CURLOPT_NOPROGRESS, 0L); result = curl_easy_perform(pooh.curl); } diff --git a/tests/libtest/lib674.c b/tests/libtest/lib674.c index 11fb8be84922..761ee3e16f7c 100644 --- a/tests/libtest/lib674.c +++ b/tests/libtest/lib674.c @@ -62,7 +62,7 @@ static CURLcode test_lib674(const char *URL) if(result) { curl_mfprintf(stderr, "%s:%d curl_easy_perform() failed " "with code %d (%s)\n", - __FILE__, __LINE__, result, curl_easy_strerror(result)); + __FILE__, __LINE__, (int)result, curl_easy_strerror(result)); goto test_cleanup; } diff --git a/tests/libtest/lib676.c b/tests/libtest/lib676.c index 0f7fc735f9b4..e08f2d4b1230 100644 --- a/tests/libtest/lib676.c +++ b/tests/libtest/lib676.c @@ -40,11 +40,11 @@ static CURLcode test_lib676(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_USERAGENT, "the-moo agent next generation"); - test_setopt(curl, CURLOPT_COOKIEFILE, libtest_arg2); - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_USERAGENT, "the-moo agent next generation"); + easy_setopt(curl, CURLOPT_COOKIEFILE, libtest_arg2); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); result = curl_easy_perform(curl); if(result) { @@ -53,7 +53,7 @@ static CURLcode test_lib676(const char *URL) } /* now clear the cookies */ - test_setopt(curl, CURLOPT_COOKIEFILE, NULL); + easy_setopt(curl, CURLOPT_COOKIEFILE, NULL); result = curl_easy_perform(curl); if(result) diff --git a/tests/libtest/lib677.c b/tests/libtest/lib677.c index fdb7294caaa0..106fbb26034f 100644 --- a/tests/libtest/lib677.c +++ b/tests/libtest/lib677.c @@ -84,7 +84,7 @@ static CURLcode test_lib677(const char *URL) } else if(ec) { curl_mfprintf(stderr, "curl_easy_send() failed, with code %d (%s)\n", - ec, curl_easy_strerror(ec)); + (int)ec, curl_easy_strerror(ec)); result = ec; goto test_cleanup; } @@ -105,7 +105,7 @@ static CURLcode test_lib677(const char *URL) } else if(ec) { curl_mfprintf(stderr, "curl_easy_recv() failed, with code %d (%s)\n", - ec, curl_easy_strerror(ec)); + (int)ec, curl_easy_strerror(ec)); result = ec; goto test_cleanup; } diff --git a/tests/libtest/lib678.c b/tests/libtest/lib678.c index 73aa4ee7e972..90295f42a8d6 100644 --- a/tests/libtest/lib678.c +++ b/tests/libtest/lib678.c @@ -47,9 +47,8 @@ static int loadfile(const char *filename, void **filedata, size_t *filesize) continue_reading = FALSE; curlx_fclose(fInCert); if(!continue_reading) { - curlx_free(data); + curlx_safefree(data); datasize = 0; - data = NULL; } } } @@ -97,7 +96,7 @@ static CURLcode test_lib678(const char *URL) curl_global_init(CURL_GLOBAL_DEFAULT); if(!strcmp("check", URL)) { CURLcode w = CURLE_OK; - struct curl_blob blob = { 0 }; + struct curl_blob blob = { CURL_UNCONST("silly"), 5, 0 }; CURL *curl = curl_easy_init(); if(curl) { w = curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob); diff --git a/tests/libtest/lib694.c b/tests/libtest/lib694.c index 83d5e8f4169f..1b2c6e2d8bb2 100644 --- a/tests/libtest/lib694.c +++ b/tests/libtest/lib694.c @@ -42,12 +42,12 @@ static CURLcode test_lib694(const char *URL) return TEST_ERR_MAJOR_BAD; } - test_setopt(curl, CURLOPT_URL, URL); - test_setopt(curl, CURLOPT_HEADER, 1L); - test_setopt(curl, CURLOPT_VERBOSE, 1L); - test_setopt(curl, CURLOPT_HTTPAUTH, + easy_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_HEADER, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC | CURLAUTH_DIGEST | CURLAUTH_NTLM); - test_setopt(curl, CURLOPT_USERPWD, "me:password"); + easy_setopt(curl, CURLOPT_USERPWD, "me:password"); do { @@ -63,7 +63,7 @@ static CURLcode test_lib694(const char *URL) } /* set a new URL for the second, so that we do not restart NTLM */ - test_setopt(curl, CURLOPT_URL, libtest_arg2); + easy_setopt(curl, CURLOPT_URL, libtest_arg2); } while(!result && ++count < 2); test_cleanup: diff --git a/tests/libtest/lib695.c b/tests/libtest/lib695.c index 90a658c598f1..de8986d88859 100644 --- a/tests/libtest/lib695.c +++ b/tests/libtest/lib695.c @@ -23,14 +23,6 @@ ***************************************************************************/ #include "first.h" -/* write callback that does nothing */ -static size_t write_it(char *ptr, size_t size, size_t nmemb, void *userdata) -{ - (void)ptr; - (void)userdata; - return size * nmemb; -} - static CURLcode test_lib695(const char *URL) { CURL *curl = NULL; @@ -51,13 +43,13 @@ static CURLcode test_lib695(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Do not write anything. */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_it); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); /* Build the first mime structure. */ mime1 = curl_mime_init(curl); @@ -88,7 +80,7 @@ static CURLcode test_lib695(const char *URL) result = curl_mime_subparts(part, mime1); if(result != CURLE_OK) - curl_mfprintf(stderr, "curl_mime_subparts() failed: %sn", + curl_mfprintf(stderr, "curl_mime_subparts() failed: %s\n", curl_easy_strerror(result)); else { mime1 = NULL; diff --git a/tests/libtest/lib753.c b/tests/libtest/lib753.c index a83f4c6b40ac..6260afb6bc87 100644 --- a/tests/libtest/lib753.c +++ b/tests/libtest/lib753.c @@ -134,8 +134,8 @@ static CURLcode test_lib753(const char *URL) * 1. Violently cleanup EASY1 *without* removing it from the multi * handle first. This MUST discard the connection that EASY1 holds, * as EASY1 is not DONE at this point. - * With the env var CURL_FTP_PWD_STOP set, the connection will - * have no outstanding data at this point. This would allow + * With the env var CURL_FTP_PWD_STOP set, the connection has + * no outstanding data at this point. This would allow * reuse if the connection is not terminated by the cleanup. * 2. Add EASY2 for the same URL and observe in the expected result * that the connection is NOT reused, e.g. all FTP commands diff --git a/tests/libtest/lib757.c b/tests/libtest/lib757.c index 88f863746ae9..325f3b7b6b96 100644 --- a/tests/libtest/lib757.c +++ b/tests/libtest/lib757.c @@ -23,14 +23,6 @@ ***************************************************************************/ #include "first.h" -/* write callback that does nothing */ -static size_t write_757(char *ptr, size_t size, size_t nmemb, void *userdata) -{ - (void)ptr; - (void)userdata; - return size * nmemb; -} - static const char t757_data[] = "fun-times"; static size_t const t757_datalen = sizeof(t757_data) - 1; @@ -72,13 +64,13 @@ static CURLcode test_lib757(const char *URL) curl = curl_easy_init(); /* First set the URL that is about to receive our POST. */ - test_setopt(curl, CURLOPT_URL, URL); + easy_setopt(curl, CURLOPT_URL, URL); /* get verbose debug output please */ - test_setopt(curl, CURLOPT_VERBOSE, 1L); + easy_setopt(curl, CURLOPT_VERBOSE, 1L); /* Do not write anything. */ - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_757); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, tutil_throwaway_cb); /* Build the first mime structure. */ mime1 = curl_mime_init(curl); @@ -109,7 +101,7 @@ static CURLcode test_lib757(const char *URL) result = curl_mime_subparts(part, mime1); if(result != CURLE_OK) - curl_mfprintf(stderr, "curl_mime_subparts() failed: %sn", + curl_mfprintf(stderr, "curl_mime_subparts() failed: %s\n", curl_easy_strerror(result)); else { mime1 = NULL; diff --git a/tests/libtest/lib758.c b/tests/libtest/lib758.c index b6642cb130aa..2fa8ef3ab110 100644 --- a/tests/libtest/lib758.c +++ b/tests/libtest/lib758.c @@ -249,8 +249,8 @@ static int t758_checkForCompletion(CURLM *multi, int *success) *success = 0; } else { - curl_mfprintf(stderr, "%s got an unexpected message from curl: %i\n", - t758_tag(), message->msg); + curl_mfprintf(stderr, "%s got an unexpected message from curl: %d\n", + t758_tag(), (int)message->msg); result = 1; *success = 0; } @@ -293,7 +293,7 @@ static CURLMcode t758_saction(CURLM *multi, curl_socket_t s, CURLMcode mresult = curl_multi_socket_action(multi, s, evBitmask, &numhandles); if(mresult != CURLM_OK) { - curl_mfprintf(stderr, "%s curl error on %s (%i) %s\n", + curl_mfprintf(stderr, "%s curl error on %s (%d) %s\n", t758_tag(), info, mresult, curl_multi_strerror(mresult)); } return mresult; @@ -352,7 +352,7 @@ static CURLcode t758_one(const char *URL, int timer_fail_at, easy_init(curl); debug_config.nohex = TRUE; debug_config.tracetime = TRUE; - test_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); + easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_config); easy_setopt(curl, CURLOPT_DEBUGFUNCTION, libtest_debug_cb); easy_setopt(curl, CURLOPT_VERBOSE, 1L); @@ -386,7 +386,7 @@ static CURLcode t758_one(const char *URL, int timer_fail_at, if(t758_ctx.fake_async_cert_verification_pending && !t758_ctx.fake_async_cert_verification_finished) { - /* the wakeup socket will be monitored */ + /* the wakeup socket is monitored */ if((sockets.read.count > 1) || sockets.write.count) { t758_msg("during verification there should be no sockets scheduled"); result = TEST_ERR_MAJOR_BAD; @@ -491,7 +491,7 @@ static CURLcode test_lib758(const char *URL) callback calls */ result = t758_one(URL, 0, 0); /* no callback fails */ if(result) - curl_mfprintf(stderr, "%s FAILED: %d\n", t758_tag(), result); + curl_mfprintf(stderr, "%s FAILED: %d\n", t758_tag(), (int)result); return result; } diff --git a/tests/libtest/mk-lib1521.pl b/tests/libtest/mk-lib1521.pl index f3d0c088e86d..6590d5257a7d 100755 --- a/tests/libtest/mk-lib1521.pl +++ b/tests/libtest/mk-lib1521.pl @@ -42,6 +42,7 @@ 'CURLOPT_DNS_LOCAL_IP4', 'CURLOPT_DNS_LOCAL_IP6', 'CURLOPT_DNS_SERVERS', + 'CURLOPT_ECH', 'CURLOPT_PROXY_TLSAUTH_TYPE', 'CURLOPT_SSLENGINE', 'CURLOPT_TLSAUTH_TYPE', @@ -243,20 +244,20 @@ static void errlongzero(const char *name, CURLcode result, int lineno) { curl_mprintf("%s set to 0 returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void errlong(const char *name, CURLcode result, int lineno) { $allowednumerrors curl_mprintf("%s set to non-zero returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void errneg(const char *name, CURLcode result, int lineno) { curl_mprintf("%s set to -1 returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void errstring(const char *name, CURLcode result, int lineno) @@ -265,25 +266,25 @@ when given a strange string input */ $allowedstringerrors curl_mprintf("%s set to a string returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void err(const char *name, CURLcode result, int lineno) { curl_mprintf("%s returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void errnull(const char *name, CURLcode result, int lineno) { curl_mprintf("%s set to NULL returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static void t1521_geterr(const char *name, CURLcode result, int lineno) { curl_mprintf("CURLINFO_%s returned %d, \\"%s\\" on line %d\\n", - name, result, curl_easy_strerror(result), lineno); + name, (int)result, curl_easy_strerror(result), lineno); } static curl_progress_callback progresscb; @@ -441,10 +442,10 @@ next; } if($_ =~ /^CURLOPT(?:DEPRECATED)?\(([^ ]*), ([^ ]*), (\d*)[,)]/) { - my ($name, $type, $val)=($1, $2, $3); - my $w=" "; - my $w2="$w$w"; - my $w3="$w$w$w"; + my ($name, $type, $val) = ($1, $2, $3); + my $w = " "; + my $w2 = "$w$w"; + my $w3 = "$w$w$w"; my $opt = $name; $opt =~ s/^CURLOPT_//; my $exists = "${w}{\n"; @@ -562,7 +563,7 @@ } elsif($type eq "CURLOPTTYPE_FUNCTIONPOINT") { if($name =~ /([^ ]*)FUNCTION/) { - my $l=lc($1); + my $l = lc($1); $l =~ s/^curlopt_//; print $fh "${fpref}\n$i${l}cb);\n$fcheck"; } @@ -593,7 +594,7 @@ } elsif($infomode && ($_ =~ /^CURLINFO_([^ ]*) *= *CURLINFO_([^ ]*)/)) { - my ($info, $type)=($1, $2); + my ($info, $type) = ($1, $2); my $c = " result = curl_easy_getinfo(curl, CURLINFO_$info,"; my $check = " if(result)\n t1521_geterr(\"$info\", result, __LINE__);\n"; if($type eq "STRING") { diff --git a/tests/libtest/test1013.pl b/tests/libtest/test1013.pl index e03e99af4364..b6bd5be7ff03 100755 --- a/tests/libtest/test1013.pl +++ b/tests/libtest/test1013.pl @@ -32,11 +32,11 @@ exit 3; } -my $what=$ARGV[2]; +my $what = $ARGV[2]; # Read the output of curl --version -my $curl_protocols=""; -open(CURL, $ARGV[1]) || die "Cannot get curl $what list\n"; +my $curl_protocols = ""; +open(CURL, $ARGV[1]) or die "Cannot get curl $what list\n"; while() { $curl_protocols = $_ if(/$what:/i); } @@ -48,7 +48,7 @@ # Read the output of curl-config my @curl_config; -open(CURLCONFIG, '-|', 'sh', $ARGV[0], "--$what") || die "Cannot get curl-config $what list\n"; +open(CURLCONFIG, '-|', 'sh', $ARGV[0], "--$what") or die "Cannot get curl-config $what list\n"; while() { chomp; $_ = lc($_) if($what eq "protocols"); # accept uppercase protocols in curl-config diff --git a/tests/libtest/test1022.pl b/tests/libtest/test1022.pl index c25ae3ce6c28..a74a180e1d04 100755 --- a/tests/libtest/test1022.pl +++ b/tests/libtest/test1022.pl @@ -31,10 +31,10 @@ exit 3; } -my $what=$ARGV[2]; +my $what = $ARGV[2]; # Read the output of curl --version -open(CURL, $ARGV[1]) || die "Cannot open curl --version list in $ARGV[1]\n"; +open(CURL, $ARGV[1]) or die "Cannot open curl --version list in $ARGV[1]\n"; $_ = ; chomp; /libcurl\/([\.\d]+((-DEV)|(-rc\d)|(-\d+))?)/; @@ -44,10 +44,10 @@ my $curlconfigversion; # Read the output of curl-config --version/--vernum -open(CURLCONFIG, '-|', 'sh', $ARGV[0], "--$what") || die "Cannot get curl-config --$what list\n"; +open(CURLCONFIG, '-|', 'sh', $ARGV[0], "--$what") or die "Cannot get curl-config --$what list\n"; $_ = ; chomp; -my $filever=$_; +my $filever = $_; if($what eq "version") { if($filever =~ /^libcurl ([\.\d]+((-DEV)|(-rc\d)|(-\d+))?)$/) { $curlconfigversion = $1; diff --git a/tests/libtest/test610.pl b/tests/libtest/test610.pl index 10f665620ae2..af63c3e6b3b6 100755 --- a/tests/libtest/test610.pl +++ b/tests/libtest/test610.pl @@ -36,20 +36,20 @@ my $cmd = shift @ARGV; my $arg = shift @ARGV; if($cmd eq "mkdir") { - mkdir $arg || die "$!"; + mkdir $arg or die "$!"; } elsif($cmd eq "rmdir") { - rmdir $arg || die "$!"; + rmdir $arg or die "$!"; } elsif($cmd eq "rm") { - unlink $arg || die "$!"; + unlink $arg or die "$!"; } elsif($cmd eq "move") { my $arg2 = shift @ARGV; - move($arg,$arg2) || die "$!"; + move($arg,$arg2) or die "$!"; } elsif($cmd eq "gone") { - ! -e $arg || die "Path $arg exists"; + ! -e $arg or die "Path $arg exists"; } else { print "Unsupported command $cmd\n"; exit 1; diff --git a/tests/libtest/test613.pl b/tests/libtest/test613.pl index f653b36c1328..a404bb613925 100755 --- a/tests/libtest/test613.pl +++ b/tests/libtest/test613.pl @@ -41,16 +41,16 @@ sub errout { if($ARGV[0] eq "prepare") { my $dirname = $ARGV[1]; - mkdir $dirname || errout "$!"; + mkdir $dirname or errout "$!"; chdir $dirname; # Create the files in alphabetical order, to increase the chances # of receiving a consistent set of directory contents regardless # of whether the server alphabetizes the results or not. - mkdir "asubdir" || errout "$!"; + mkdir "asubdir" or errout "$!"; chmod 0777, "asubdir"; - open(FILE, ">plainfile.txt") || errout "$!"; + open(FILE, ">plainfile.txt") or errout "$!"; binmode FILE; print FILE "Test file to support curl test suite\n"; close(FILE); @@ -59,7 +59,7 @@ sub errout { utime time, timegm(0,0,12,1,0,100), "plainfile.txt"; chmod 0666, "plainfile.txt"; - open(FILE, ">emptyfile.txt") || errout "$!"; + open(FILE, ">emptyfile.txt") or errout "$!"; binmode FILE; close(FILE); # The mtime is specifically chosen to be an even number so that it can be @@ -67,7 +67,7 @@ sub errout { utime time, timegm(0,0,12,1,0,100), "emptyfile.txt"; chmod 0666, "emptyfile.txt"; - open(FILE, ">rofile.txt") || errout "$!"; + open(FILE, ">rofile.txt") or errout "$!"; binmode FILE; print FILE "Read-only test file to support curl test suite\n"; close(FILE); @@ -94,7 +94,7 @@ sub errout { unlink "$dirname/plainfile.txt"; rmdir "$dirname/asubdir"; - rmdir $dirname || die "$!"; + rmdir $dirname or die "$!"; if($#ARGV >= 3) { # Verify mtime if requested my $checkfile = $ARGV[2]; @@ -120,7 +120,7 @@ sub errout { # -r-?r-?r-? 12 U U 47 Dec 31 2000 rofile.txt my @canondir; - open(IN, "<$logfile") || die "$!"; + open(IN, "<$logfile") or die "$!"; while() { /^(.)(..).(..).(..).\s*(\S+)\s+\S+\s+\S+\s+(\S+)\s+(\S+\s+\S+\s+\S+)\s+(.*)$/; if($1 eq "d") { @@ -140,7 +140,7 @@ sub errout { my $line = sprintf("%s%s???????%5d U U %15d %s %s\n", $1,$2,$5,$6,$7,$8); push @canondir, $line; } else { - # Unexpected format; just pass it through and let the test fail + # Unexpected format; pass it through and let the test fail push @canondir, $_; } } @@ -148,7 +148,7 @@ sub errout { @canondir = sort {substr($a, 57) cmp substr($b, 57)} @canondir; my $newfile = $logfile . ".new"; - open(OUT, ">$newfile") || die "$!"; + open(OUT, ">$newfile") or die "$!"; print OUT join('', @canondir); close(OUT); diff --git a/tests/libtest/testutil.c b/tests/libtest/testutil.c index 81f174a647c2..7336a736f697 100644 --- a/tests/libtest/testutil.c +++ b/tests/libtest/testutil.c @@ -21,7 +21,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "testutil.h" +#include "first.h" /* build request URL */ char *tutil_suburl(const char *base, int i) @@ -44,3 +44,14 @@ void tutil_rlim2str(char *buf, size_t len, rlim_t val) curl_msnprintf(buf, len, "%lu", (unsigned long)val); } #endif + +/* + * Handy CURLOPT_WRITEFUNCTION for tests that do not need to keep received + * data. + */ +size_t tutil_throwaway_cb(char *data, size_t n, size_t l, void *userp) +{ + (void)data; + (void)userp; + return n * l; +} diff --git a/tests/libtest/testutil.h b/tests/libtest/testutil.h index e66dbdc1c17a..9799752cd50b 100644 --- a/tests/libtest/testutil.h +++ b/tests/libtest/testutil.h @@ -23,7 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -#include "first.h" /* build request URL */ char *tutil_suburl(const char *base, int i); @@ -36,4 +35,10 @@ char *tutil_suburl(const char *base, int i); void tutil_rlim2str(char *buf, size_t len, rlim_t val); #endif +/* + * Handy CURLOPT_WRITEFUNCTION for tests that do not need to keep received + * data. + */ +size_t tutil_throwaway_cb(char *data, size_t n, size_t l, void *userp); + #endif /* HEADER_CURL_LIBTEST_TESTUTIL_H */ diff --git a/tests/libtest/unitcheck.h b/tests/libtest/unitcheck.h index 462f8b45f32c..bc616d135b48 100644 --- a/tests/libtest/unitcheck.h +++ b/tests/libtest/unitcheck.h @@ -48,7 +48,7 @@ #define verify_memory(dynamic, check, len) \ do { \ - if((dynamic) && memcmp(dynamic, check, len)) { \ + if(memcmp(dynamic, check, len)) { \ curl_mfprintf(stderr, "%s:%d Memory buffer FAILED match size %d. " \ "'%s' is not\n", __FILE__, __LINE__, len, \ hexdump((const unsigned char *)(check), len)); \ diff --git a/tests/memanalyze.pl b/tests/memanalyze.pl index e30b263c4128..15382e527af4 100755 --- a/tests/memanalyze.pl +++ b/tests/memanalyze.pl @@ -34,16 +34,16 @@ while(@ARGV) { if($ARGV[0] eq "-v") { - $verbose=1; + $verbose = 1; shift @ARGV; } elsif($ARGV[0] eq "-t") { - $trace=1; + $trace = 1; shift @ARGV; } elsif($ARGV[0] eq "-l") { # only show what alloc that caused a memlimit failure - $showlimit=1; + $showlimit = 1; shift @ARGV; } else { diff --git a/tests/memanalyzer.pm b/tests/memanalyzer.pm index 2a395da2e0fd..110b39d24111 100644 --- a/tests/memanalyzer.pm +++ b/tests/memanalyzer.pm @@ -46,7 +46,7 @@ my $memsum; my $maxmem; sub newtotal { - my ($newtot)=@_; + my ($newtot) = @_; # count a max here if($newtot > $maxmem) { @@ -152,8 +152,8 @@ sub memanalyze { newtotal($totalmem); $frees++; - $sizeataddr{$addr}=-1; # set -1 to mark as freed - $getmem{$addr}="$source:$linenum"; + $sizeataddr{$addr} = -1; # set -1 to mark as freed + $getmem{$addr} = "$source:$linenum"; } } elsif($function =~ /malloc\((\d*)\) = 0x([0-9a-f]*)/) { @@ -177,7 +177,7 @@ sub memanalyze { newtotal($totalmem); $mallocs++; - $getmem{$addr}="$source:$linenum"; + $getmem{$addr} = "$source:$linenum"; } elsif($function =~ /calloc\((\d*),(\d*)\) = 0x([0-9a-f]*)/) { $size = $1 * $2; @@ -203,7 +203,7 @@ sub memanalyze { newtotal($totalmem); $callocs++; - $getmem{$addr}="$source:$linenum"; + $getmem{$addr} = "$source:$linenum"; } elsif($function =~ /realloc\((\(nil\)|0x([0-9a-f]*)), (\d*)\) = 0x([0-9a-f]*)/) { my ($oldaddr, $newsize, $newaddr) = ($2, $3, $4); @@ -231,7 +231,7 @@ sub memanalyze { newtotal($totalmem); $reallocs++; - $getmem{$newaddr}="$source:$linenum"; + $getmem{$newaddr} = "$source:$linenum"; } elsif($function =~ /strdup\(0x([0-9a-f]*)\) \((\d*)\) = 0x([0-9a-f]*)/) { # strdup(a5b50) (8) = df7c0 @@ -258,8 +258,8 @@ sub memanalyze { $dup = $1; $size = $2; $addr = $3; - $getmem{$addr}="$source:$linenum"; - $sizeataddr{$addr}=$size; + $getmem{$addr} = "$source:$linenum"; + $sizeataddr{$addr} = $size; $totalmem += $size; $memsum += $size; @@ -306,7 +306,7 @@ sub memanalyze { push @res, "Close without open: $line\n"; } else { - $filedes{$1}=0; # closed now + $filedes{$1} = 0; # closed now $openfile--; } } diff --git a/tests/negtelnetserver.py b/tests/negtelnetserver.py index c31fc033aaa0..a9a4880018f5 100755 --- a/tests/negtelnetserver.py +++ b/tests/negtelnetserver.py @@ -84,7 +84,7 @@ def handle(self): neg.send_wont("NAWS") # Get the data passed through the negotiator - data = neg.recv(4*1024) + data = neg.recv(4 * 1024) log.debug("Incoming data: %r", data) if VERIFIED_REQ.encode('utf-8') in data: @@ -106,7 +106,7 @@ def handle(self): # put some effort into making a clean socket shutdown # that does not give the client ECONNRESET self.request.settimeout(0.1) - self.request.recv(4*1024) + self.request.recv(4 * 1024) self.request.shutdown(socket.SHUT_RDWR) except IOError: @@ -167,7 +167,7 @@ def no_neg(self, byte_int, buffer): log.debug("Starting negotiation (IAC)") self.state = self.START_NEG else: - # Just append the incoming byte to the buffer + # Append the incoming byte to the buffer buffer.append(byte_int) def start_neg(self, byte_int): diff --git a/tests/pathhelp.pm b/tests/pathhelp.pm index e481255a3a55..500e43f12576 100644 --- a/tests/pathhelp.pm +++ b/tests/pathhelp.pm @@ -197,7 +197,7 @@ sub dirsepadd { # This does the same thing as String::ShellQuote but does not need a package. # sub shell_quote { - my ($s)=@_; + my ($s) = @_; if($^O eq 'MSWin32') { $s = '"' . $s . '"'; } diff --git a/tests/processhelp.pm b/tests/processhelp.pm index ee5fc52d6dc4..434a14f7cf3a 100644 --- a/tests/processhelp.pm +++ b/tests/processhelp.pm @@ -75,9 +75,8 @@ use globalconfig qw( ####################################################################### # pidfromfile returns the pid stored in the given pidfile. The value -# of the returned pid will never be a negative value. It will be zero -# on any file related error or if a pid can not be extracted from the -# given file. +# of the returned pid is never a negative value. It is zero on any file +# related error or if a pid can not be extracted from the given file. # sub pidfromfile { my $pidfile = $_[0]; @@ -115,9 +114,9 @@ sub winpid_to_pid { ####################################################################### # pidexists checks if a process with a given pid exists and is alive. -# This will return the positive pid if the process exists and is alive. -# This will return the negative pid if the process exists differently. -# This will return 0 if the process could not be found. +# This returns the positive pid if the process exists and is alive. +# This returns the negative pid if the process exists differently. +# This returns 0 if the process could not be found. # sub pidexists { my $pid = $_[0]; @@ -136,7 +135,7 @@ sub pidexists { } else { my $filter = "PID eq $pid"; # https://ss64.com/nt/tasklist.html - my $result = `tasklist -fi \"$filter\" 2>$dev_null`; + my $result = qx(tasklist -fi \"$filter\" 2>$dev_null); if(index($result, $pid) != -1) { return -$pid; } @@ -170,12 +169,12 @@ sub pidterm { Win32::Process::KillProcess($pid, 0); } else { # https://ss64.com/nt/tasklist.html - my $result = `tasklist -v -fo list -fi "PID eq $pid" 2>&1`; + my $result = qx(tasklist -v -fo list -fi "PID eq $pid" 2>&1); $result =~ s/\r//g; $result =~ s/\n/ | /g; print "Task info for $pid before taskkill: '$result'\n"; - $result = `powershell -Command "Get-CimInstance -ClassName Win32_Process -Filter 'ParentProcessId=$pid' | Select ProcessId,ParentProcessId,Name,CommandLine"`; + $result = qx(powershell -Command "Get-CimInstance -ClassName Win32_Process -Filter 'ParentProcessId=$pid' | Select ProcessId,ParentProcessId,Name,CommandLine"); $result =~ s/\r//g; print "Task child processes for $pid before taskkill:\n"; print "$result\n"; @@ -221,12 +220,12 @@ sub pidkill { Win32::Process::KillProcess($pid, 0); } else { # https://ss64.com/nt/tasklist.html - my $result = `tasklist -v -fo list -fi "PID eq $pid" 2>&1`; + my $result = qx(tasklist -v -fo list -fi "PID eq $pid" 2>&1); $result =~ s/\r//g; $result =~ s/\n/ | /g; print "Task info for $pid before taskkill: '$result'\n"; - $result = `powershell -Command "Get-CimInstance -ClassName Win32_Process -Filter 'ParentProcessId=$pid' | Select ProcessId,ParentProcessId,Name,CommandLine"`; + $result = qx(powershell -Command "Get-CimInstance -ClassName Win32_Process -Filter 'ParentProcessId=$pid' | Select ProcessId,ParentProcessId,Name,CommandLine"); $result =~ s/\r//g; print "Task child processes for $pid before taskkill:\n"; print "$result\n"; @@ -291,11 +290,11 @@ sub pidwait { ####################################################################### # processexists checks if a process with the pid stored in the given -# pidfile exists and is alive. This will return 0 on any file related +# pidfile exists and is alive. This returns 0 on any file related # error or if a pid can not be extracted from the given file. When a # process with the same pid as the one extracted from the given file # is currently alive this returns that positive pid. Otherwise, when -# the process is not alive, will return the negative value of the pid. +# the process is not alive, it returns the negative value of the pid. # sub processexists { use POSIX ":sys_wait_h"; @@ -342,8 +341,8 @@ sub killpid { @requested = sort({$a <=> $b} @requested); } for(my $i = scalar(@requested) - 2; $i >= 0; $i--) { - if($requested[$i] == $requested[$i+1]) { - splice @requested, $i+1, 1; + if($requested[$i] == $requested[$i + 1]) { + splice @requested, $i + 1, 1; } } diff --git a/tests/requirements.txt b/tests/requirements.txt index 0d08837bd745..cb30e3e29827 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: curl -impacket>=0.11.0,<=0.13.0 +impacket==0.13.0 diff --git a/tests/rtspserver.pl b/tests/rtspserver.pl index d23ed7e6373e..cbe914eee087 100755 --- a/tests/rtspserver.pl +++ b/tests/rtspserver.pl @@ -38,7 +38,7 @@ BEGIN ); my $verbose = 0; # set to 1 for debugging -my $port = 8990; # just a default +my $port = 8990; # a default my $ipvnum = 4; # default IP version of rtsp server my $idnum = 1; # default rtsp server instance number my $proto = 'rtsp'; # protocol the rtsp server speaks diff --git a/tests/runner.pm b/tests/runner.pm index 8ebb855ca635..9e514f0e238d 100644 --- a/tests/runner.pm +++ b/tests/runner.pm @@ -23,13 +23,13 @@ ########################################################################### # This module contains entry points to run a single test. runner_init -# determines whether they will run in a separate process or in the process of -# the caller. The relevant interface is asynchronous so it will work in either +# determines whether they run in a separate process or in the process of +# the caller. The relevant interface is asynchronous so it works in either # case. Program arguments are marshalled and then written to the end of a pipe # (in controlleripccall) which is later read from and the arguments # unmarshalled (in ipcrecv) before the desired function is called normally. # The function return values are then marshalled and written into another pipe -# (again in ipcrecv) when is later read from and unmarshalled (in runnerar) +# (again in ipcrecv) which is later read from and unmarshalled (in runnerar) # before being returned to the caller. package runner; @@ -241,8 +241,10 @@ sub runner_init { } $controllerw{$thisrunnerid} = $thiscontrollerw; - $runnerr = $thisrunnerr; - $runnerw = $thisrunnerw; + if(!$multiprocess) { + $runnerr = $thisrunnerr; + $runnerw = $thisrunnerw; + } $controllerr{$thisrunnerid} = $thiscontrollerr; return $thisrunnerid; @@ -251,7 +253,7 @@ sub runner_init { ####################################################################### # Loop to execute incoming IPC calls until the shutdown call sub event_loop { - while() { + while(1) { if(ipcrecv()) { last; } @@ -647,11 +649,11 @@ sub singletest_preprocess { @entiretest = prepro($testnum, @entiretest); # save the new version - open(my $fulltesth, ">", $otest) || die "Failure writing test file"; + open(my $fulltesth, ">", $otest) or die "Failure writing test file"; foreach my $bytes (@entiretest) { print $fulltesth pack('a*', $bytes) or die "Failed to print '$bytes': $!"; } - close($fulltesth) || die "Failure writing test file"; + close($fulltesth) or die "Failure writing test file"; # in case the process changed the file, reload it loadtest("$LOGDIR/test${testnum}"); @@ -666,7 +668,7 @@ sub singletest_setenv { if($s =~ /([^=]*)(.*)/) { my ($var, $content) = ($1, $2); # remember current setting, to restore it once test runs - $oldenv{$var} = ($ENV{$var}) ? "$ENV{$var}" : 'notset'; + $oldenv{$var} = $ENV{$var} ? $ENV{$var} : 'notset'; if($content =~ /^=(.*)/) { # assign it @@ -698,9 +700,9 @@ sub singletest_precheck { if($cmd) { my @p = split(/ /, $cmd); if($p[0] !~ /\//) { - # the first word, the command, does not contain a slash so - # we will scan the "improved" PATH to find the command to - # be able to run it + # the first word, the command, does not contain a slash so we + # scan the "improved" PATH to find the command to be able + # to run it my $fullp = checktestcmd($p[0]); if($fullp) { @@ -709,7 +711,10 @@ sub singletest_precheck { $cmd = join(" ", @p); } - my @o = `$cmd 2> $LOGDIR/precheck-$testnum`; + # provide an environment variable + $ENV{'CURL_TESTNUM'} = $testnum; + + my @o = qx($cmd 2> $LOGDIR/precheck-$testnum); if($o[0]) { $why = $o[0]; $why =~ s/[\r\n]//g; @@ -993,17 +998,16 @@ sub singletest_run { logmsg "$CMDLINE\n"; } - open(my $cmdlog, ">", "$LOGDIR/$CURLLOG") || - die "Failure writing log file"; + open(my $cmdlog, ">", "$LOGDIR/$CURLLOG") or die "Failure writing log file"; print $cmdlog "$CMDLINE\n"; - close($cmdlog) || die "Failure writing log file"; + close($cmdlog) or die "Failure writing log file"; my $dumped_core; my $cmdres; if($gdbthis) { my $gdbinit = "$TESTDIR/gdbinit$testnum"; - open(my $gdbcmd, ">", "$LOGDIR/gdbcmd") || die "Failure writing gdb file"; + open(my $gdbcmd, ">", "$LOGDIR/gdbcmd") or die "Failure writing gdb file"; if($gdbthis == 1) { # gdb mode print $gdbcmd "set args $cmdargs\n"; @@ -1014,7 +1018,7 @@ sub singletest_run { # lldb mode print $gdbcmd "set args $cmdargs\n"; } - close($gdbcmd) || die "Failure writing gdb file"; + close($gdbcmd) or die "Failure writing gdb file"; } # Flush output. @@ -1078,9 +1082,9 @@ sub singletest_clean { logmsg "core dumped\n"; if(0 && $gdb) { logmsg "running gdb for post-mortem analysis:\n"; - open(my $gdbcmd, ">", "$LOGDIR/gdbcmd2") || die "Failure writing gdb file"; + open(my $gdbcmd, ">", "$LOGDIR/gdbcmd2") or die "Failure writing gdb file"; print $gdbcmd "bt\n"; - close($gdbcmd) || die "Failure writing gdb file"; + close($gdbcmd) or die "Failure writing gdb file"; runclient("$gdb --directory libtest -x $LOGDIR/gdbcmd2 -batch " . shell_quote($DBGCURL) . " core "); # unlink("$LOGDIR/gdbcmd2"); } @@ -1331,6 +1335,7 @@ sub controlleripccall { my $margs = freeze \@_; # Send IPC call via pipe + length($margs) < 1000 or die "A large IPC write risks blocking on some platforms"; my $err; while(! defined ($err = syswrite($controllerw{$runnerid}, (pack "L", length($margs)) . $margs)) || $err <= 0) { if((!defined $err && ! $!{EINTR}) || (defined $err && $err == 0)) { @@ -1401,11 +1406,11 @@ sub runnerar_ready { $maxfileno = $fd; } } - $maxfileno || die "Internal error: no runners are available to wait on\n"; + $maxfileno or die "Internal error: no runners are available to wait on\n"; # Wait for any pipe from any runner to be ready # This may be interrupted and return EINTR, but this is ignored and the - # caller will need to later call this function again. + # caller needs to later call this function again. # TODO: this is relatively slow with hundreds of fds my $e_in = $r_in; if(select(my $r_out = $r_in, undef, my $e_out = $e_in, $blocking) >= 1) { @@ -1446,7 +1451,7 @@ sub ipcrecv { if((!defined $err && ! $!{EINTR}) || (defined $err && $err == 0)) { # pipe has closed; controller is gone and we must exit runnerabort(); - # Special case: no response will be forthcoming + # Special case: no response is forthcoming return 1; } # system call was interrupted, probably by ^C; restart it so we stay in sync @@ -1457,7 +1462,7 @@ sub ipcrecv { if((!defined $err && ! $!{EINTR}) || (defined $err && $err == 0)) { # pipe has closed; controller is gone and we must exit runnerabort(); - # Special case: no response will be forthcoming + # Special case: no response is forthcoming return 1; } # system call was interrupted, probably by ^C; restart it so we stay in sync @@ -1474,7 +1479,7 @@ sub ipcrecv { my @res; if($funcname eq "runner_shutdown") { runner_shutdown(@$argsarrayref); - # Special case: no response will be forthcoming + # Special case: no response is forthcoming return 1; } elsif($funcname eq "runner_stopservers") { @@ -1497,7 +1502,7 @@ sub ipcrecv { if((!defined $err && ! $!{EINTR}) || (defined $err && $err == 0)) { # pipe has closed; controller is gone and we must exit runnerabort(); - # Special case: no response will be forthcoming + # Special case: no response is forthcoming return 1; } # system call was interrupted, probably by ^C; restart it so we stay in sync diff --git a/tests/runtests.pl b/tests/runtests.pl index 050875dc5b35..61949614b970 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -44,7 +44,7 @@ # the contents of the tests/server/ directory must be from the host # running the test suite, while the rest must be from the host running curl. # -# Note that even with these changes a number of tests will still fail (mainly +# Note that even with these changes a number of tests still fail (mainly # to do with cookies, those that set environment variables, or those that # do more than touch the file system in a or # section). These can be added to the $TESTCASES line below, @@ -54,7 +54,7 @@ # to check the remote system's PATH, and the places in the code where # the curl binary is read directly to determine its type also need to be # fixed. As long as the -g option is never given, and the -n is always -# given, this will not be a problem. +# given, this is not a problem. use strict; use warnings; @@ -106,16 +106,16 @@ BEGIN my %custom_skip_reasons; -my $ACURL=$VCURL; # what curl binary to use to talk to APIs (relevant for CI) - # ACURL is handy to set to the system one for reliability -my $CURLCONFIG="../curl-config"; # curl-config from current build +my $ACURL = $VCURL; # what curl binary to use to talk to APIs (relevant for CI) + # ACURL is handy to set to the system one for reliability +my $CURLCONFIG = "../curl-config"; # curl-config from current build # Normally, all test cases should be run, but at times it is handy to # run a particular one: -my $TESTCASES="all"; +my $TESTCASES = "all"; # To run specific test cases, set them like: -# $TESTCASES="1 2 3 7 8"; +# $TESTCASES = "1 2 3 7 8"; ####################################################################### # No variables below this point should need to be modified @@ -128,7 +128,7 @@ BEGIN my $start; # time at which testing started my $args; # command-line arguments -my $uname_release = `uname -r`; +my $uname_release = qx(uname -r); my $is_wsl = $uname_release =~ /Microsoft$/; my $http_ipv6; # set if HTTP server has IPv6 support @@ -146,6 +146,9 @@ BEGIN my %ignored; # ignored results of test cases my %ignoretestcodes; # if test results are to be ignored +my @global_strip_stderr; # global patterns added to stripfile before stderr check +my @global_strip_file; # global patterns added to stripfile before file check + my $passedign; # tests passed with results ignored my $timestats; # time stamping and stats generation @@ -179,7 +182,6 @@ BEGIN # my $short; my $no_debuginfod; -my $keepoutfiles; # keep stdout and stderr files after tests my $postmortem; # display detailed info about failed tests my $run_disabled; # run the specific tests even if listed in DISABLED my $scrambleorder; @@ -214,7 +216,7 @@ sub logmsg { # enable logmsg buffering for the given runner ID # sub logmsg_bufferfortest { - my ($runnerid)=@_; + my ($runnerid) = @_; if($jobs) { # Only enable buffering in multiprocess mode $singletest_bufferedrunner = $runnerid; @@ -321,7 +323,7 @@ sub catch_usr1 { } if($ENV{"NGHTTPX"}) { my $cmd = "\"$ENV{'NGHTTPX'}\" -v 2>$dev_null"; - my $nghttpx_version=join(' ', `$cmd`); + my $nghttpx_version = join(' ', qx($cmd)); $nghttpx_h3 = $nghttpx_version =~ /nghttp3\//; chomp $nghttpx_h3; } @@ -353,8 +355,7 @@ sub cleardir { my $file; # Get all files - opendir(my $dh, $dir) || - return 0; # cannot open dir + opendir(my $dh, $dir) or return 0; # cannot open dir while($file = readdir($dh)) { # Do not clear the $PIDDIR or $LOCKDIR since those need to live beyond # one test @@ -381,15 +382,15 @@ sub cleardir { } ####################################################################### -# Given two array references, this function will store them in two temporary +# Given two array references, this function stores them in two temporary # files, run 'diff' on them, store the result and return the diff output! sub showdiff { - my ($logdir, $firstref, $secondref)=@_; + my ($logdir, $firstref, $secondref) = @_; - my $file1="$logdir/check-generated"; - my $file2="$logdir/check-expected"; + my $file1 = "$logdir/check-generated"; + my $file2 = "$logdir/check-expected"; - open(my $temp, ">", $file1) || die "Failure writing diff file"; + open(my $temp, ">", $file1) or die "Failure writing diff file"; for(@$firstref) { my $l = $_; $l =~ s/\r/[CR]/g; @@ -398,9 +399,9 @@ sub showdiff { print $temp $l; print $temp "\n"; } - close($temp) || die "Failure writing diff file"; + close($temp) or die "Failure writing diff file"; - open($temp, ">", $file2) || die "Failure writing diff file"; + open($temp, ">", $file2) or die "Failure writing diff file"; for(@$secondref) { my $l = $_; $l =~ s/\r/[CR]/g; @@ -409,11 +410,11 @@ sub showdiff { print $temp $l; print $temp "\n"; } - close($temp) || die "Failure writing diff file"; - my @out = `diff -u $file2 $file1 2>$dev_null`; + close($temp) or die "Failure writing diff file"; + my @out = qx(diff -u $file2 $file1 2>$dev_null); if(!$out[0]) { - @out = `diff -c $file2 $file1 2>$dev_null`; + @out = qx(diff -c $file2 $file1 2>$dev_null); if(!$out[0]) { logmsg "Failed to show diff. The diff tool may be missing.\n"; } @@ -427,7 +428,7 @@ sub showdiff { # some pattern that is allowed to differ, output test results # sub compare { - my ($runnerid, $testnum, $testname, $subject, $firstref, $secondref)=@_; + my ($runnerid, $testnum, $testname, $subject, $firstref, $secondref) = @_; my $result = compareparts($firstref, $secondref); @@ -454,21 +455,21 @@ sub compare { ####################################################################### # Numeric-sort words in a string sub numsortwords { - my ($string)=@_; + my ($string) = @_; return join(' ', sort { $a <=> $b } split(' ', $string)); } ####################################################################### # Parse and store the protocols in curl's Protocols: line sub parseprotocols { - my ($line)=@_; + my ($line) = @_; @protocols = split(' ', lc($line)); # Generate a "proto-ipv6" version of each protocol to match the # IPv6 name and a "proto-unix" to match the variant which # uses Unix domain sockets. This works even if support is not - # compiled in because the test will fail. + # compiled in because the test fails. push @protocols, map(("$_-ipv6", "$_-unix"), @protocols); # 'http-proxy' is used in test cases to do CONNECT through @@ -512,13 +513,13 @@ sub checksystemfeatures { my $libcurl; my $versretval; my $versnoexec; - my @version=(); + my @version = (); my @disabled; my $dis = ""; - my $curlverout="$LOGDIR/curlverout.log"; - my $curlvererr="$LOGDIR/curlvererr.log"; - my $versioncmd=exerunner() . shell_quote($CURL) . " --version 1>$curlverout 2>$curlvererr"; + my $curlverout = "$LOGDIR/curlverout.log"; + my $curlvererr = "$LOGDIR/curlvererr.log"; + my $versioncmd = exerunner() . shell_quote($CURL) . " --version 1>$curlverout 2>$curlvererr"; unlink($curlverout); unlink($curlvererr); @@ -549,7 +550,7 @@ sub checksystemfeatures { $dis = join(", ", @disabled); } - $resolver="stock"; + $resolver = "stock"; for(@version) { chomp; @@ -558,7 +559,7 @@ sub checksystemfeatures { $CURLVERSION = $1; $CURLVERNUM = $CURLVERSION; $CURLVERNUM =~ s/^([0-9.]+)(.*)/$1/; # leading dots and numbers - $curl =~ s/^(.*)(libcurl.*)/$1/g || die "Failure determining curl binary version"; + $curl =~ s/^(.*)(libcurl.*)/$1/g or die "Failure determining curl binary version"; $libcurl = $2; if($curl =~ /win32|Windows|windows|mingw(32|64)/) { @@ -589,7 +590,7 @@ sub checksystemfeatures { $feature{"wolfssl"} = 1; $feature{"SSLpinning"} = 1; } - elsif($libcurl =~ /\s(BoringSSL|AWS-LC)\b/i) { + elsif($libcurl =~ /\s(AWS-LC|BoringSSL)\b/i) { # OpenSSL compatible API $feature{"OpenSSL"} = 1; $feature{"SSLpinning"} = 1; @@ -610,7 +611,7 @@ sub checksystemfeatures { } if($libcurl =~ /ares/i) { $feature{"c-ares"} = 1; - $resolver="c-ares"; + $resolver = "c-ares"; } if($libcurl =~ /nghttp2/i) { # nghttp2 supports h2c @@ -635,7 +636,7 @@ sub checksystemfeatures { $feature{"sshkeyalgo"} = ($ENV{'CURL_TEST_SSH_KEYALGO'} and $ENV{'CURL_TEST_SSH_KEYALGO'} =~ /^(?:rsa|ecdsa|ed25519)$/) ? $ENV{'CURL_TEST_SSH_KEYALGO'} : 'rsa'; # Detect simple cases of default libssh configuration files ending up - # setting `StrictHostKeyChecking no`. include files, quoted values, + # setting 'StrictHostKeyChecking no'. include files, quoted values, # '=value' format not implemented. $feature{"badlibssh"} = 0; foreach my $libssh_configfile (('/etc/ssh/ssh_config', $ENV{'HOME'} . '/.ssh/config')) { @@ -664,10 +665,12 @@ sub checksystemfeatures { $feature{"TrackMemory"} = $feat =~ /\bDebug/; # curl was built with --enable-debug $feature{"Debug"} = $feat =~ /\bDebug/; - # ssl enabled + # SSL enabled $feature{"SSL"} = $feat =~ /SSL/i; - # multiple ssl backends available. + # multiple SSL backends available. $feature{"MultiSSL"} = $feat =~ /MultiSSL/i; + # embedded CA certificate bundle + $feature{"CAcert"} = $feat =~ /CAcert/i; # large file support $feature{"Largefile"} = $feat =~ /Largefile/i; # IDN support @@ -707,7 +710,7 @@ sub checksystemfeatures { if(!$feature{"c-ares"} || $feature{"asyn-rr"}) { # this means threaded resolver $feature{"threaded-resolver"} = 1; - $resolver="threaded"; + $resolver = "threaded"; # does not count as "real" c-ares $feature{"c-ares"} = 0; @@ -748,7 +751,7 @@ sub checksystemfeatures { my $add_httptls; for(@protocols) { if($_ =~ /^https(-ipv6|)$/) { - $add_httptls=1; + $add_httptls = 1; last; } } @@ -759,6 +762,10 @@ sub checksystemfeatures { } } + if($torture) { + $feature{"torture"} = 1; + } + if(!$curl) { logmsg "unable to get curl's version, further details are:\n"; logmsg "issued command: \n"; @@ -797,7 +804,7 @@ sub checksystemfeatures { # check if the HTTP server has it! my $cmd = server_exe('sws')." --version"; - my @sws = `$cmd`; + my @sws = qx($cmd); if($sws[0] =~ /IPv6/) { # HTTP server has IPv6 support! $http_ipv6 = 1; @@ -805,7 +812,7 @@ sub checksystemfeatures { # check if the FTP server has it! $cmd = server_exe('sockfilt')." --version"; - @sws = `$cmd`; + @sws = qx($cmd); if($sws[0] =~ /IPv6/) { # FTP server has IPv6 support! $ftp_ipv6 = 1; @@ -815,10 +822,17 @@ sub checksystemfeatures { if($feature{"UnixSockets"}) { # client has Unix sockets support, check whether the HTTP server has it my $cmd = server_exe('sws')." --version"; - my @sws = `$cmd`; + my @sws = qx($cmd); $http_unix = 1 if($sws[0] =~ /unix/); } + # strip line from stderr and file output to not confuse tests + if($feature{"CAcert"}) { + my $strip_cacert = 's/^Note: Using embedded CA bundle.*\n//'; + push @global_strip_stderr, $strip_cacert; + push @global_strip_file, $strip_cacert; + } + open(my $manh, "-|", shell_quote($CURL) . " -M 2>&1"); while(my $s = <$manh>) { if($s =~ /built-in manual was disabled at build-time/) { @@ -853,11 +867,11 @@ sub checksystemfeatures { "TrackMemory feature (--enable-debug)"; } - my $hostname=join(' ', runclientoutput("hostname")); + my $hostname = join(' ', runclientoutput("hostname")); chomp $hostname; - my $hosttype=join(' ', runclientoutput("uname -a")); + my $hosttype = join(' ', runclientoutput("uname -a")); chomp $hosttype; - my $hostos=$^O; + my $hostos = $^O; # display summary information about curl and the test host logmsg("********* System characteristics ******** \n", @@ -1004,7 +1018,7 @@ sub citest_finishtestrun { # add one set of test timings from the runner to global set sub updatetesttimings { - my ($testnum, %testtimings)=@_; + my ($testnum, %testtimings) = @_; if(defined $testtimings{"timeprepini"}) { $timeprepini{$testnum} = $testtimings{"timeprepini"}; @@ -1053,7 +1067,7 @@ sub getrunnerlogdir { # Verify that this test case should be run sub singletest_shouldrun { my $testnum = $_[0]; - my $why; # why the test will not be run + my $why; # why the test is not run my $errorreturncode = 1; # 1 means normal error, 2 means ignored error my @what; # what features are needed @@ -1193,6 +1207,8 @@ sub singletest_shouldrun { return ($why, $errorreturncode); } +my %allnames; + ####################################################################### # Print the test name and count tests sub singletest_count { @@ -1201,7 +1217,7 @@ sub singletest_count { if($why && !$listonly) { # there is a problem, count it as "skipped" $skipped{$why}++; - $teststat[$testnum]=$why; # store reason for this test case + $teststat[$testnum] = $why; # store reason for this test case if(!$short) { if($skipped{$why} <= 3) { @@ -1225,6 +1241,17 @@ sub singletest_count { if($listonly) { timestampskippedevents($testnum); } + else { + if(exists $allnames{$testname} && + ($allnames{$testname} != $testnum)) { + logmsg sprintf("ERROR: test %d has duplicate test name: \"%s\". ". + "The same as test %d\n", $testnum, + $testname, $allnames{$testname}); + exit 1; + } + # store which test that uses this name + $allnames{$testname} = $testnum; + } return 0; } @@ -1238,7 +1265,7 @@ sub normalize_text { ####################################################################### # Verify test succeeded sub singletest_check { - my ($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind)=@_; + my ($runnerid, $testnum, $cmdres, $CURLOUT, $tool, $usedvalgrind) = @_; # Skip all the verification on torture tests if($torture) { @@ -1250,11 +1277,12 @@ sub singletest_check { my $logdir = getrunnerlogdir($runnerid); my @err = getpart("verify", "errorcode"); my $errorcode = $err[0] || "0"; - my $ok=""; + my $ok = ""; my $res; chomp $errorcode; my $testname= (getpart("client", "name"))[0]; chomp $testname; + # what parts to cut off from stdout/stderr my @stripfile = getpart("verify", "stripfile"); @@ -1264,7 +1292,7 @@ sub singletest_check { my $loadfile = $hash{'loadfile'}; if($loadfile) { - open(my $tmp, "<", $loadfile) || die "Cannot open file $loadfile: $!"; + open(my $tmp, "<", $loadfile) or die "Cannot open file $loadfile: $!"; @validstdout = <$tmp>; close($tmp); @@ -1293,7 +1321,7 @@ sub singletest_check { } # get the mode attribute - my $filemode=$hash{'mode'}; + my $filemode = $hash{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@validstdout); normalize_text(\@actual); @@ -1329,7 +1357,7 @@ sub singletest_check { # verify redirected stderr my @actual = loadarray(stderrfilename($logdir, $testnum)); - foreach my $strip (@stripfile) { + foreach my $strip (@global_strip_stderr, @stripfile) { chomp $strip; my @newgen; for(@actual) { @@ -1347,7 +1375,7 @@ sub singletest_check { my %hash = getpartattr("verify", "stderr"); # get the mode attribute - my $filemode=$hash{'mode'}; + my $filemode = $hash{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@validstderr); normalize_text(\@actual); @@ -1466,7 +1494,7 @@ sub singletest_check { if(@replycheckpart) { my %replycheckpartattr = getpartattr("reply", "datacheck".$partsuffix); # get the mode attribute - my $filemode=$replycheckpartattr{'mode'}; + my $filemode = $replycheckpartattr{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@replycheckpart); } @@ -1497,7 +1525,7 @@ sub singletest_check { } } # get the mode attribute - my $filemode=$replyattr{'mode'}; + my $filemode = $replyattr{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@reply); } @@ -1516,7 +1544,7 @@ sub singletest_check { my @out = loadarray($CURLOUT); # get the mode attribute - my $filemode=$replyattr{'mode'}; + my $filemode = $replyattr{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@out); } @@ -1621,12 +1649,12 @@ sub singletest_check { my $outputok; for my $partsuffix (('', '1', '2', '3', '4')) { - my @outfile=getpart("verify", "file".$partsuffix); + my @outfile = getpart("verify", "file".$partsuffix); if(@outfile || partexists("verify", "file".$partsuffix) ) { # we are supposed to verify a dynamically generated file! my %hash = getpartattr("verify", "file".$partsuffix); - my $filename=$hash{'name'}; + my $filename = $hash{'name'}; if(!$filename) { logmsg " $testnum: IGNORED: section verify=>file$partsuffix ". "has no name attribute\n"; @@ -1634,7 +1662,7 @@ sub singletest_check { logmsg "ERROR: runner $runnerid seems to have died\n"; } else { - # TODO: this is a blocking call that will stall the controller, + # TODO: this is a blocking call that stalls the controller, if($verbose) { logmsg "WARNING: blocking call in async function\n"; } @@ -1651,12 +1679,12 @@ sub singletest_check { $timevrfyend{$testnum} = Time::HiRes::time(); return -1; } - my @generated=loadarray($filename); + my @generated = loadarray($filename); # what parts to cut off from the file my @stripfilepar = getpart("verify", "stripfile".$partsuffix); - my $filemode=$hash{'mode'}; + my $filemode = $hash{'mode'}; if($filemode && ($filemode eq "text")) { normalize_text(\@outfile); normalize_text(\@generated); @@ -1670,7 +1698,7 @@ sub singletest_check { } } - for my $strip (@stripfilepar) { + for my $strip (@global_strip_file, @stripfilepar) { chomp $strip; my @newgen; for(@generated) { @@ -1716,7 +1744,7 @@ sub singletest_check { if(@dnsd) { # we are supposed to verify a dynamically generated file! my %hash = getpartattr("verify", "dns"); - my $hostname=$hash{'host'}; + my $hostname = $hash{'host'}; # Verify the sent DNS requests my @out = loadarray("$logdir/dnsd.input"); @@ -1724,8 +1752,7 @@ sub singletest_check { my @sout = sort @out; if($hostname) { - # when a hostname is set, we filter out requests to just this - # pattern + # when a hostname is set, we filter out requests to this pattern @sout = grep {/$hostname/} @sout; } @@ -1772,12 +1799,12 @@ sub singletest_check { } else { my @memdata = memanalyze("$logdir/$MEMDUMP", 0, 0, 0); - my $leak=0; + my $leak = 0; for(@memdata) { if($_ ne "") { # well it could be other memory problems as well, but # we call it leak for short here - $leak=1; + $leak = 1; } } if($leak) { @@ -1922,17 +1949,17 @@ sub singletest_check { ####################################################################### # Report a successful test sub singletest_success { - my ($testnum, $count, $total, $errorreturncode)=@_; + my ($testnum, $count, $total, $errorreturncode) = @_; my $sofar= time()-$start; my $esttotal = $sofar/$count * $total; my $estleft = $esttotal - $sofar; - my $timeleft=sprintf("remaining: %02d:%02d", - $estleft/60, - $estleft%60); + my $timeleft = sprintf("remaining: %02d:%02d", + $estleft / 60, + $estleft % 60); my $took = $timevrfyend{$testnum} - $timeprepini{$testnum}; my $duration = sprintf("duration: %02d:%02d", - $sofar/60, $sofar%60); + $sofar / 60, $sofar % 60); if(!$automakestyle) { logmsg sprintf("OK (%-3d out of %-3d, %s, took %.3fs, %s)\n", $count, $total, $timeleft, $took, $duration); @@ -1943,7 +1970,7 @@ sub singletest_success { logmsg "PASS: $testnum - $testname\n"; } - if($errorreturncode==2) { + if($errorreturncode == 2) { # ignored test success $passedign .= "$testnum "; logmsg "Warning: test$testnum result is ignored, but passed!\n"; @@ -1959,7 +1986,7 @@ sub singletest_success { # arrived. # sub singletest { - my ($runnerid, $testnum, $count, $total)=@_; + my ($runnerid, $testnum, $count, $total) = @_; # start buffering logmsg; stop it on return logmsg_bufferfortest($runnerid); @@ -2063,7 +2090,7 @@ sub singletest { logmsg $logs; updatetesttimings($testnum, %$testtimings); if($error == -1) { - # no further verification will occur + # no further verification occurs $timevrfyend{$testnum} = Time::HiRes::time(); my $err = ignoreresultcode($testnum); # Submit the test case result with the CI environment @@ -2083,7 +2110,7 @@ sub singletest { return ($error, 0); } elsif($error > 0) { - # no further verification will occur + # no further verification occurs $timevrfyend{$testnum} = Time::HiRes::time(); # Submit the test case result with the CI environment citest_finishtest($testnum, $error); @@ -2280,7 +2307,7 @@ sub runtimestats { # 0=unknown test, 1=use test result, 2=ignore test result # sub ignoreresultcode { - my ($testnum)=@_; + my ($testnum) = @_; if(defined $ignoretestcodes{$testnum}) { return $ignoretestcodes{$testnum}; } @@ -2291,7 +2318,7 @@ sub ignoreresultcode { # Put the given runner ID onto the queue of runners ready for a new task # sub runnerready { - my ($runnerid)=@_; + my ($runnerid) = @_; push @runnersidle, $runnerid; } @@ -2299,7 +2326,7 @@ sub runnerready { # Create test runners # sub createrunners { - my ($numrunners)=@_; + my ($numrunners) = @_; if(! $numrunners) { $numrunners++; } @@ -2317,8 +2344,8 @@ sub createrunners { # Pick a test runner for the given test # sub pickrunner { - my ($testnum)=@_; - scalar(@runnersidle) || die "No runners available"; + my ($testnum) = @_; + scalar(@runnersidle) or die "No runners available"; return pop @runnersidle; } @@ -2337,49 +2364,49 @@ sub pickrunner { $args = join(' ', @ARGV); $valgrind = checktestcmd("valgrind"); -my $number=0; -my $fromnum=-1; +my $number = 0; +my $fromnum = -1; my @testthis; while(@ARGV) { if($ARGV[0] eq "-v") { # verbose output - $verbose=1; + $verbose = 1; } elsif($ARGV[0] eq "-c") { # use this path to curl instead of default - $DBGCURL=$CURL=$ARGV[1]; + $DBGCURL = $CURL = $ARGV[1]; shift @ARGV; } elsif($ARGV[0] eq "-vc") { # use this path to a curl used to verify servers # Particularly useful when you introduce a crashing bug somewhere in - # the development version as then it will not be able to run any tests + # the development version as then it is not able to run any tests # since it cannot verify the servers! - $VCURL=shell_quote($ARGV[1]); + $VCURL = shell_quote($ARGV[1]); shift @ARGV; } elsif($ARGV[0] eq "-ac") { # use this curl only to talk to APIs (currently only CI test APIs) - $ACURL=shell_quote($ARGV[1]); + $ACURL = shell_quote($ARGV[1]); shift @ARGV; } elsif($ARGV[0] eq "-d") { # have the servers display protocol output - $debugprotocol=1; + $debugprotocol = 1; } elsif(($ARGV[0] eq "-e") || ($ARGV[0] eq "--test-event")) { # run the tests cases event based if possible - $run_event_based=1; + $run_event_based = 1; } elsif($ARGV[0] eq "--test-duphandle") { # run the tests with --test-duphandle - $run_duphandle=1; + $run_duphandle = 1; } elsif($ARGV[0] eq "-f") { # force - run the test case even if listed in DISABLED - $run_disabled=1; + $run_disabled = 1; } elsif($ARGV[0] eq "-E") { # load additional reasons to skip tests @@ -2405,33 +2432,33 @@ sub pickrunner { } elsif($ARGV[0] eq "-g") { # run this test with gdb - $gdbthis=1; + $gdbthis = 1; } elsif($ARGV[0] eq "-gl") { # run this test with lldb - $gdbthis=2; + $gdbthis = 2; } elsif($ARGV[0] eq "-gw") { # run this test with windowed gdb - $gdbthis=1; - $gdbxwin=1; + $gdbthis = 1; + $gdbxwin = 1; } elsif($ARGV[0] eq "-s") { # short output - $short=1; + $short = 1; } elsif($ARGV[0] eq "-am") { # automake-style output - $short=1; - $automakestyle=1; + $short = 1; + $automakestyle = 1; } elsif($ARGV[0] =~ /-m=(\d+)/) { - my ($num)=($1); - $maxtime=$num; + my ($num) = ($1); + $maxtime = $num; } elsif($ARGV[0] =~ /--min=(\d+)/) { - my ($num)=($1); - $mintotal=$num; + my ($num) = ($1); + $mintotal = $num; } elsif($ARGV[0] eq "-n") { # no valgrind @@ -2443,11 +2470,11 @@ sub pickrunner { } elsif($ARGV[0] eq "-R") { # execute in scrambled order - $scrambleorder=1; + $scrambleorder = 1; } elsif($ARGV[0] =~ /^-t(.*)/) { # torture - $torture=1; + $torture = 1; my $xtra = $1; if($xtra =~ s/(\d+)$//) { @@ -2457,8 +2484,8 @@ sub pickrunner { elsif($ARGV[0] =~ /--shallow=(\d+)/) { # Fail no more than this amount per tests when running # torture. - my ($num)=($1); - $shallow=$num; + my ($num) = ($1); + $shallow = $num; } elsif($ARGV[0] =~ /--repeat=(\d+)/) { # Repeat-run the given tests this many times @@ -2474,7 +2501,7 @@ sub pickrunner { } elsif($ARGV[0] eq "-a") { # continue anyway, even if a test fail - $anyway=1; + $anyway = 1; } elsif($ARGV[0] eq "-o") { shift @ARGV; @@ -2486,11 +2513,11 @@ sub pickrunner { } } elsif($ARGV[0] eq "-p") { - $postmortem=1; + $postmortem = 1; } elsif($ARGV[0] eq "-P") { shift @ARGV; - $proxy_address=$ARGV[0]; + $proxy_address = $ARGV[0]; } elsif($ARGV[0] eq "-L") { # require additional library file @@ -2499,23 +2526,19 @@ sub pickrunner { } elsif($ARGV[0] eq "-l") { # lists the test case names only - $listonly=1; + $listonly = 1; } elsif($ARGV[0] eq "--buildinfo") { - $buildinfo=1; + $buildinfo = 1; } elsif($ARGV[0] =~ /^-j(.*)/) { # parallel jobs - $jobs=1; + $jobs = 1; my $xtra = $1; if($xtra =~ s/(\d+)$//) { $jobs = $1; } } - elsif($ARGV[0] eq "-k") { - # keep stdout and stderr files after tests - $keepoutfiles=1; - } elsif($ARGV[0] eq "-r") { # run time statistics needs Time::HiRes if($Time::HiRes::VERSION) { @@ -2527,8 +2550,8 @@ sub pickrunner { keys(%timetoolend) = 2000; keys(%timesrvrlog) = 2000; keys(%timevrfyend) = 2000; - $timestats=1; - $fullstats=0; + $timestats = 1; + $fullstats = 0; } } elsif($ARGV[0] eq "-rf") { @@ -2542,13 +2565,13 @@ sub pickrunner { keys(%timetoolend) = 2000; keys(%timesrvrlog) = 2000; keys(%timevrfyend) = 2000; - $timestats=1; - $fullstats=1; + $timestats = 1; + $fullstats = 1; } } elsif($ARGV[0] eq "-u") { # error instead of warning on server unexpectedly alive - $err_unexpected=1; + $err_unexpected = 1; } elsif(($ARGV[0] eq "-h") || ($ARGV[0] eq "--help")) { # show help text @@ -2568,7 +2591,6 @@ sub pickrunner { -gw run the test case with gdb as a windowed application -h this help text -j[N] spawn this number of processes to run tests (default 0) - -k keep stdout and stderr files present after tests -L path require an additional perl library file to replace certain functions -l list all test case names/descriptions -m=[seconds] set timeout for curl commands in tests @@ -2613,24 +2635,24 @@ sub pickrunner { } } elsif($ARGV[0] =~ /^to$/i) { - $fromnum = $number+1; + $fromnum = $number + 1; } elsif($ARGV[0] =~ /^!(\d+)/) { $fromnum = -1; - $disabled{$1}=$1; + $disabled{$1} = $1; } elsif($ARGV[0] =~ /^~(\d+)/) { $fromnum = -1; - $ignored{$1}=$1; + $ignored{$1} = $1; } elsif($ARGV[0] =~ /^!(.+)/) { - $disabled_keywords{lc($1)}=$1; + $disabled_keywords{lc($1)} = $1; } elsif($ARGV[0] =~ /^~(.+)/) { - $ignored_keywords{lc($1)}=$1; + $ignored_keywords{lc($1)} = $1; } elsif($ARGV[0] =~ /^([-[{a-zA-Z].*)/) { - $enabled_keywords{lc($1)}=$1; + $enabled_keywords{lc($1)} = $1; } else { print "Unknown option: $ARGV[0]\n"; @@ -2642,15 +2664,14 @@ sub pickrunner { delete $ENV{'DEBUGINFOD_URLS'} if($ENV{'DEBUGINFOD_URLS'} && $no_debuginfod); if(!$randseed) { - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = - localtime(time); + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time); # seed of the month. December 2019 becomes 201912 - $randseed = ($year+1900)*100 + $mon+1; + $randseed = ($year + 1900) * 100 + $mon + 1; print "Using curl: $CURL\n"; - open(my $curlvh, "-|", exerunner() . shell_quote($CURL) . " --version 2>$dev_null") || + open(my $curlvh, "-|", exerunner() . shell_quote($CURL) . " --version 2>$dev_null") or die "could not get curl version!"; my @c = <$curlvh>; - close($curlvh) || die "could not get curl version!"; + close($curlvh) or die "could not get curl version!"; # use the first line of output and get the md5 out of it my $str = md5($c[0]); $randseed += unpack('S', $str); # unsigned 16-bit value @@ -2658,7 +2679,7 @@ sub pickrunner { srand $randseed; if(@testthis && ($testthis[0] ne "")) { - $TESTCASES=join(" ", @testthis); + $TESTCASES = join(" ", @testthis); } if($valgrind) { @@ -2675,30 +2696,30 @@ sub pickrunner { # since valgrind 2.1.x, '--tool' option is mandatory # use it, if it is supported by the version installed on the system # (this happened in 2003, so we could probably do not need to care about - # that old version any longer and just delete this check) + # that old version any longer and delete this check) runclient("valgrind --help 2>&1 | grep -- --tool >$dev_null 2>&1"); if(($? >> 8)) { - $valgrind_tool=""; + $valgrind_tool = ""; } open(my $curlh, "<", $CURL); my $l = <$curlh>; if($l =~ /^\#\!/) { # A shell script. This is typically when built with libtool, - $valgrind="../libtool --mode=execute $valgrind"; + $valgrind = "../libtool --mode=execute $valgrind"; } close($curlh); # valgrind 3 renamed the --logfile option to --log-file!!! # (this happened in 2005, so we could probably do not need to care about - # that old version any longer and just delete this check) - my $ver=join(' ', runclientoutput("valgrind --version")); + # that old version any longer and delete this check) + my $ver = join(' ', runclientoutput("valgrind --version")); # cut off all but digits and dots $ver =~ s/[^0-9.]//g; if($ver =~ /^(\d+)/) { $ver = $1; if($ver < 3) { - $valgrind_logfile="--logfile"; + $valgrind_logfile = "--logfile"; } } } @@ -2801,7 +2822,7 @@ sub disabledtests { for my $t (@pp) { if($t =~ /(\d+)/) { my ($n) = $1; - $disabled{$n}=$n; # disable this test number + $disabled{$n} = $n; # disable this test number if(! -f "$srcdir/data/test$n") { print STDERR "WARNING! Non-existing test $n in $file!\n"; # fail hard to make user notice @@ -2827,11 +2848,11 @@ sub disabledtests { if($TESTCASES eq "all") { # Get all commands and find out their test numbers - opendir(DIR, $TESTDIR) || die "cannot opendir $TESTDIR: $!"; + opendir(DIR, $TESTDIR) or die "cannot opendir $TESTDIR: $!"; my @cmds = grep { /^test([0-9]+)$/ && -f "$TESTDIR/$_" } readdir(DIR); closedir(DIR); - $TESTCASES=""; # start with no test cases + $TESTCASES = ""; # start with no test cases # cut off everything but the digits for(@cmds) { @@ -2843,17 +2864,17 @@ sub disabledtests { # skip disabled test cases my $why = "configured as DISABLED"; $skipped{$why}++; - $teststat[$n]=$why; # store reason for this test case + $teststat[$n] = $why; # store reason for this test case next; } $TESTCASES .= " $n"; } } else { - my $verified=""; + my $verified = ""; for(split(" ", $TESTCASES)) { if(-e "$TESTDIR/test$_") { - $verified.="$_ "; + $verified .= "$_ "; } } if($verified eq "") { @@ -2881,7 +2902,7 @@ sub disabledtests { } my $r = rand @all; push @rand, $all[$r]; - $all[$r]=""; + $all[$r] = ""; $TESTCASES = join(" ", @all); } $TESTCASES = join(" ", @rand); @@ -2890,7 +2911,7 @@ sub disabledtests { # Display the contents of the given file. Line endings are canonicalized # and excessively long files are elided sub displaylogcontent { - my ($file)=@_; + my ($file) = @_; if(open(my $single, "<", $file)) { my $linecount = 0; my $truncate; @@ -2928,10 +2949,9 @@ sub displaylogcontent { } sub displaylogs { - my ($runnerid, $testnum)=@_; + my ($runnerid, $testnum) = @_; my $logdir = getrunnerlogdir($runnerid); - opendir(DIR, $logdir) || - die "cannot open dir: $!"; + opendir(DIR, $logdir) or die "cannot open dir: $!"; my @logs = readdir(DIR); closedir(DIR); @@ -2992,15 +3012,15 @@ sub displaylogs { my $failed; my $failedign; my $failedre; -my $ok=0; -my $ign=0; -my $total=0; -my $executed=0; -my $retry_done=0; -my $lasttest=0; +my $ok = 0; +my $ign = 0; +my $total = 0; +my $executed = 0; +my $retry_done = 0; +my $lasttest = 0; my @at = split(" ", $TESTCASES); -my $count=0; -my $endwaitcnt=0; +my $count = 0; +my $endwaitcnt = 0; $start = time(); @@ -3010,7 +3030,7 @@ sub displaylogs { $lasttest = $testnum if($testnum > $lasttest); my ($why, $errorreturncode) = singletest_shouldrun($testnum); if($why || $listonly) { - # Display test name now--test will be completely skipped later + # Display test name now--test is completely skipped later my $error = singletest_count($testnum, $why); next; } @@ -3054,7 +3074,7 @@ sub displaylogs { $retry_left = $retry; } -while() { +while(1) { # check the abort flag if($globalabort) { logmsg singletest_dumplogs(); @@ -3127,7 +3147,7 @@ sub displaylogs { $endwaitcnt = 0; # This runner is ready to be serviced my $testnum = $runnersrunning{$ridready}; - defined $testnum || die "Internal error: test for runner $ridready unknown"; + defined $testnum or die "Internal error: test for runner $ridready unknown"; delete $runnersrunning{$ridready}; my ($error, $again) = singletest($ridready, $testnum, $countforrunner{$ridready}, $totaltests); if($again) { @@ -3212,11 +3232,11 @@ sub displaylogs { $endwaitcnt += $runnerwait; if($endwaitcnt >= 10) { # Once all tests have been scheduled on a runner at the end of a test - # run, we just wait for their results to come in. If we are still + # run, we wait for their results to come in. If we are still # waiting after a couple of minutes ($endwaitcnt multiplied by - # $runnerwait, plus $jobs because that number will not time out), display - # the same test runner status as we give with a SIGUSR1. This will - # likely point to a single test that has hung. + # $runnerwait, plus $jobs because that number does not time out), display + # the same test runner status as we give with a SIGUSR1. This likely + # points to a single test that has hung. logmsg "Hmmm, the tests are taking a while to finish. Here is the status:\n"; catch_usr1(); $endwaitcnt = 0; @@ -3262,7 +3282,7 @@ sub displaylogs { } if(%skipped && !$short) { - my $s=0; + my $s = 0; # Temporary hash to print the restraints sorted by the number # of their occurrences my %restraints; @@ -3276,7 +3296,7 @@ sub displaylogs { # now gather all test case numbers that had this reason for being # skipped - my $c=0; + my $c = 0; my $max = 9; for(0 .. scalar @teststat) { my $t = $_; @@ -3342,7 +3362,7 @@ sub testnumdetails { logmsg "IGNORED: failed tests: $sorted\n"; } logmsg sprintf("TESTDONE: $ok tests out of $total reported OK: %d%%\n", - $ok/$total*100); + $ok / $total * 100); if($failed && ($ok != $total)) { my $failedsorted = numsortwords($failed); diff --git a/tests/secureserver.pl b/tests/secureserver.pl index 9cd963261ffc..5025d47b4b8b 100755 --- a/tests/secureserver.pl +++ b/tests/secureserver.pl @@ -24,7 +24,7 @@ #*************************************************************************** # This is the HTTPS, FTPS, POP3S, IMAPS, SMTPS, server used for curl test -# harness. Actually just a layer that runs stunnel properly using the +# harness. Actually a layer that runs stunnel properly using the # non-secure test harness servers. use strict; @@ -48,9 +48,9 @@ BEGIN my $stunnel = "stunnel"; -my $verbose=0; # set to 1 for debugging +my $verbose = 0; # set to 1 for debugging -my $accept_port = 8991; # just our default, weird enough +my $accept_port = 8991; # our default, weird enough my $target_port = 8999; # default test http-server port my $stuncert; @@ -365,12 +365,12 @@ sub exit_signal_handler { # Put an "exec" in front of the command so that the child process # keeps this child's process ID by being tied to the spawned shell. - exec("exec $cmd") || die "Cannot exec() $cmd: $!"; - # exec() will create a new process, but ties the existence of the + exec("exec $cmd") or die "Cannot exec() $cmd: $!"; + # exec() creates a new process, but ties the existence of the # new process to the parent waiting perl.exe and sh.exe processes. # exec() should never return back here to this process. We protect - # ourselves by calling die() just in case something goes really bad. + # ourselves by calling die() in case something goes really bad. die "error: exec() has returned"; } diff --git a/tests/server/dnsd.c b/tests/server/dnsd.c index 9da4eefce822..8fb899bf2686 100644 --- a/tests/server/dnsd.c +++ b/tests/server/dnsd.c @@ -23,6 +23,8 @@ ***************************************************************************/ #include "first.h" +#ifndef __AMIGA__ + static int dnsd_wrotepidfile = 0; static int dnsd_wroteportfile = 0; @@ -30,10 +32,6 @@ static int dnsd_wroteportfile = 0; #include #endif -#ifdef __AMIGA__ -#error building dnsd on AMIGA os is unsupported -#endif - static uint16_t get16bit(const unsigned char **pkt, size_t *size) { const unsigned char *p = *pkt; @@ -159,13 +157,17 @@ static int blob_add_qname(struct blob *b, const struct Curl_str *str) #define QTYPE_AAAA 28 #define QTYPE_HTTPS 0x41 +#if 0 #define HTTPS_RR_CODE_MANDATORY 0x00 +#endif #define HTTPS_RR_CODE_ALPN 0x01 #define HTTPS_RR_CODE_NO_DEF_ALPN 0x02 +#if 0 #define HTTPS_RR_CODE_PORT 0x03 #define HTTPS_RR_CODE_IPV4 0x04 #define HTTPS_RR_CODE_ECH 0x05 #define HTTPS_RR_CODE_IPV6 0x06 +#endif static const char *type2string(uint16_t qtype) { @@ -344,15 +346,20 @@ static struct resp *resp_queue; static CURLcode send_resp(curl_socket_t sock, struct resp *resp) { ssize_t rc; + int sockerr = 0; -sending: - rc = sendto(sock, (const void *)resp->body.data, (SENDTO3)resp->body.dlen, 0, - &resp->addr, resp->addrlen); - if((rc < 0) && (SOCKERRNO == SOCKEINTR)) - goto sending; - if(rc != (ssize_t)resp->body.dlen) { - logmsg("failed sending %d bytes, errno=%d\n", - (int)resp->body.dlen, SOCKERRNO); + do { + rc = sendto(sock, (const void *)resp->body.data, (SENDTO3)resp->body.dlen, + 0, &resp->addr, resp->addrlen); + } while((rc < 0) && ((sockerr = SOCKERRNO) == SOCKEINTR)); + if(rc < 0) { + char errbuf[STRERROR_LEN]; + logmsg("failed sending %zu bytes, error: (%d) %s", resp->body.dlen, + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); + return CURLE_SEND_ERROR; + } + else if(rc != (ssize_t)resp->body.dlen) { + logmsg("failed sending %zu bytes, sent: %zd", resp->body.dlen, rc); return CURLE_SEND_ERROR; } logmsg("[%d] sent response", resp->qid); @@ -488,7 +495,7 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, const unsigned char *store = ipv4_pref; if(add_answer(&resp->body, store, sizeof(ipv4_pref), QTYPE_A)) goto error; - logmsg("[%d] response A (%x) '%s'", qid, QTYPE_A, + logmsg("[%d] response A (%x) '%s'", qid, (unsigned int)QTYPE_A, curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf))); } if(!ancount_a) @@ -499,7 +506,7 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, const unsigned char *store = ipv6_pref; if(add_answer(&resp->body, store, sizeof(ipv6_pref), QTYPE_AAAA)) goto error; - logmsg("[%d] response AAAA (%x) '%s'", qid, QTYPE_AAAA, + logmsg("[%d] response AAAA (%x) '%s'", qid, (unsigned int)QTYPE_AAAA, curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf))); } if(!ancount_aaaa) @@ -512,8 +519,8 @@ create_resp(int qid, const struct sockaddr *addr, curl_socklen_t addrlen, httpsrr.dlen); goto error; } - logmsg("[%d] response HTTPS (%x), %zu bytes", qid, QTYPE_HTTPS, - httpsrr.dlen); + logmsg("[%d] response HTTPS (%x), %zu bytes", qid, + (unsigned int)QTYPE_HTTPS, httpsrr.dlen); } else logmsg("[%d] response HTTPS, no record", qid); @@ -740,7 +747,7 @@ static int test_dnsd(int argc, const char **argv) curl_socket_t sock = CURL_SOCKET_BAD; int flag; int rc; - int error; + int sockerr; char errbuf[STRERROR_LEN]; int result = 0; struct resp *resp; @@ -838,18 +845,18 @@ static int test_dnsd(int argc, const char **argv) #endif if(sock == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto dnsd_cleanup; } flag = 1; if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto dnsd_cleanup; } @@ -873,9 +880,9 @@ static int test_dnsd(int argc, const char **argv) } #endif /* USE_IPV6 */ if(rc) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error binding socket on port %hu (%d) %s", port, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto dnsd_cleanup; } @@ -895,9 +902,9 @@ static int test_dnsd(int argc, const char **argv) la_size = sizeof(localaddr.sa6); #endif if(getsockname(sock, &localaddr.sa, &la_size) < 0) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); goto dnsd_cleanup; } @@ -1043,7 +1050,7 @@ static int test_dnsd(int argc, const char **argv) } clear_resp_queue(); - restore_signal_handlers(true); + restore_signal_handlers(TRUE); if(got_exit_signal) { logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)", @@ -1059,3 +1066,12 @@ static int test_dnsd(int argc, const char **argv) logmsg("========> dnsd quits"); return result; } +#else +static int test_dnsd(int argc, const char **argv) +{ + (void)argc; + (void)argv; + fprintf(stderr, "dnsd on AmigaOS is unsupported\n"); + return 1; +} +#endif diff --git a/tests/server/first.c b/tests/server/first.c index c57617249885..31ba5f72b4df 100644 --- a/tests/server/first.c +++ b/tests/server/first.c @@ -39,7 +39,7 @@ int main(int argc, const char **argv) entry_name = argv[1]; entry_func = NULL; for(tmp = 0; s_entries[tmp].ptr; ++tmp) { - if(strcmp(entry_name, s_entries[tmp].name) == 0) { + if(!strcmp(entry_name, s_entries[tmp].name)) { entry_func = s_entries[tmp].ptr; break; } diff --git a/tests/server/first.h b/tests/server/first.h index ad5cec41b392..3bd75a2d88f0 100644 --- a/tests/server/first.h +++ b/tests/server/first.h @@ -141,6 +141,8 @@ extern int write_pidfile(const char *filename); extern int write_portfile(const char *filename, int port); extern void set_advisor_read_lock(const char *filename); extern void clear_advisor_read_lock(const char *filename); +extern void storerequest(const char *reqbuf, size_t totalsize, + const char *filename); static volatile int got_exit_signal = 0; static volatile int exit_signal = 0; #ifdef _WIN32 diff --git a/tests/server/getpart.c b/tests/server/getpart.c index fe3212a15ce7..0cc95f633366 100644 --- a/tests/server/getpart.c +++ b/tests/server/getpart.c @@ -67,7 +67,7 @@ static size_t line_length(const char *buffer, int bytestocheck) * * Calling function may call this multiple times with same 'buffer' * and 'bufsize' pointers to avoid multiple buffer allocations. Buffer - * will be reallocated and 'bufsize' increased until whole line fits in + * is reallocated and 'bufsize' increased until whole line fits in * buffer before returning it. * * Calling function is responsible to free allocated buffer. @@ -132,7 +132,7 @@ static int readline(char **buffer, size_t *bufsize, size_t *length, * decoded data, binary or whatever, to the destination. The source buffer * may not hold binary data, only a null-terminated string is valid content. * - * Destination buffer will be enlarged and relocated as needed. + * Destination buffer is enlarged and relocated as needed. * * Calling function is responsible to provide preallocated destination * buffer and also to deallocate it when no longer needed. @@ -202,7 +202,7 @@ static int decodedata(char **buf, /* dest buffer */ /* * currently there is no way to tell apart an OOM condition in * curlx_base64_decode() from zero length decoded data. For now, - * let's just assume it is an OOM condition, currently we have + * let's assume it is an OOM condition, currently we have * no input for this function that decodes to zero length data. */ free(buf64); @@ -230,8 +230,8 @@ static int decodedata(char **buf, /* dest buffer */ * Data is returned in a dynamically allocated buffer, a pointer to this data * and the size of the data is stored at the addresses that caller specifies. * - * If the returned data is a string the returned size will be the length of - * the string excluding null-termination. Otherwise it will just be the size + * If the returned data is a string the returned size is the length of + * the string excluding null-termination. Otherwise it is the size * of the returned binary data. * * Calling function is responsible to free returned buffer. diff --git a/tests/server/mqttd.c b/tests/server/mqttd.c index 0a3a6ee02499..286495a63e11 100644 --- a/tests/server/mqttd.c +++ b/tests/server/mqttd.c @@ -40,6 +40,7 @@ /* #define MQTT_MSG_PUBACK 0x40 */ #define MQTT_MSG_SUBSCRIBE 0x82 #define MQTT_MSG_SUBACK 0x90 +#define MQTT_MSG_PINGRESP 0xd0 #define MQTT_MSG_DISCONNECT 0xe0 struct mqttd_configurable { @@ -49,6 +50,8 @@ struct mqttd_configurable { bool publish_before_suback; bool short_publish; bool excessive_remaining; + bool pingresp_as_connack; /* send PINGRESP with payload instead of CONNACK */ + bool disconnect_malformed; /* DISCONNECT with nonzero remlen */ unsigned char error_connack; unsigned char remlen_connack; }; @@ -65,6 +68,8 @@ static void mqttd_resetdefaults(void) m_config.publish_before_suback = FALSE; m_config.short_publish = FALSE; m_config.excessive_remaining = FALSE; + m_config.pingresp_as_connack = FALSE; + m_config.disconnect_malformed = FALSE; m_config.error_connack = 0; m_config.remlen_connack = 0; m_config.testnum = 0; @@ -98,6 +103,14 @@ static void mqttd_getconfig(void) logmsg("short-PUBLISH set"); m_config.short_publish = TRUE; } + else if(!strcmp(key, "PINGRESP-as-CONNACK")) { + logmsg("PINGRESP-as-CONNACK set"); + m_config.pingresp_as_connack = TRUE; + } + else if(!strcmp(key, "DISCONNECT-malformed")) { + logmsg("DISCONNECT-malformed set"); + m_config.disconnect_malformed = TRUE; + } else if(!strcmp(key, "error-CONNACK")) { pval = value; if(!curlx_str_number(&pval, &num, 0xff)) { @@ -166,6 +179,16 @@ static int connack(FILE *dump, curl_socket_t fd) 0x00, 0x00 }; ssize_t rc; + const char *label = "CONNACK"; + + if(m_config.pingresp_as_connack) { + /* Send a PINGRESP (0xD0) with remaining_length=2 and payload + mimicking a successful CONNACK. MQTT 3.1.1 s. 3.13.1 requires + PINGRESP to have remaining_length=0, so this is malformed. */ + packet[0] = MQTT_MSG_PINGRESP; + label = "PINGRESP-as-CONNACK"; + logmsg("Sending malformed PINGRESP in place of CONNACK"); + } if(m_config.remlen_connack) packet[1] = m_config.remlen_connack; @@ -173,10 +196,10 @@ static int connack(FILE *dump, curl_socket_t fd) rc = swrite(fd, packet, sizeof(packet)); if(rc > 0) { - logmsg("WROTE %zd bytes [CONNACK]", rc); + logmsg("WROTE %zd bytes [%s]", rc, label); loghex(packet, rc); - logprotocol(FROM_SERVER, "CONNACK", packet[1], dump, - packet, sizeof(packet)); + logprotocol(FROM_SERVER, label, packet[1], dump, + packet, rc); } if(rc == sizeof(packet)) { return 0; @@ -222,7 +245,7 @@ static int puback(FILE *dump, curl_socket_t fd, unsigned short packetid) if(rc == sizeof(packet)) { logmsg("WROTE %zd bytes [PUBACK]", rc); loghex(packet, rc); - logprotocol(FROM_SERVER, dump, packet, rc); + logprotocol(FROM_SERVER, "PUBACK", 0, dump, packet, rc); return 0; } logmsg("Failed sending [PUBACK]"); @@ -235,15 +258,30 @@ static int disconnect(FILE *dump, curl_socket_t fd) { unsigned char packet[] = { MQTT_MSG_DISCONNECT, 0x00, + 0x00, 0x00 /* extra bytes for malformed variant */ }; - ssize_t rc = swrite(fd, packet, sizeof(packet)); - if(rc == sizeof(packet)) { - logmsg("WROTE %zd bytes [DISCONNECT]", rc); + size_t pktlen = 2; + const char *label = "DISCONNECT"; + ssize_t rc; + + if(m_config.disconnect_malformed) { + /* Send DISCONNECT with remaining_length=2 (must be 0 per spec) */ + packet[1] = 0x02; + pktlen = 4; + label = "DISCONNECT-malformed"; + logmsg("Sending malformed DISCONNECT with nonzero remaining_length"); + } + + rc = swrite(fd, packet, pktlen); + if(rc > 0) { + logmsg("WROTE %zd bytes [%s]", rc, label); loghex(packet, rc); - logprotocol(FROM_SERVER, "DISCONNECT", 0, dump, packet, rc); + logprotocol(FROM_SERVER, label, packet[1], dump, packet, rc); + } + if(rc == (ssize_t)pktlen) { return 0; } - logmsg("Failed sending [DISCONNECT]"); + logmsg("Failed sending [%s]", label); return 1; } @@ -378,10 +416,10 @@ static int publish(FILE *dump, static char topic[MAX_TOPIC_LENGTH + 1]; -static int fixedheader(curl_socket_t fd, - unsigned char *bytep, - size_t *remaining_lengthp, - size_t *remaining_length_bytesp) +static bool fixedheader(curl_socket_t fd, + unsigned char *bytep, + size_t *remaining_lengthp, + size_t *remaining_length_bytesp) { /* get the fixed header */ unsigned char buffer[10]; @@ -391,7 +429,7 @@ static int fixedheader(curl_socket_t fd, size_t i; if(rc < 2) { logmsg("READ %zd bytes [SHORT!]", rc); - return 1; /* fail */ + return FALSE; /* fail */ } logmsg("READ %zd bytes", rc); loghex(buffer, rc); @@ -404,20 +442,19 @@ static int fixedheader(curl_socket_t fd, rc = sread(fd, &buffer[i], 1); if(rc != 1) { logmsg("Remaining Length broken"); - return 1; + return FALSE; } } *remaining_lengthp = decode_length(&buffer[1], i, remaining_length_bytesp); logmsg("Remaining Length: %zu [%zu bytes]", *remaining_lengthp, *remaining_length_bytesp); - return 0; + return TRUE; } static curl_socket_t mqttit(curl_socket_t fd) { size_t buff_size = 10 * 1024; unsigned char *buffer = NULL; - ssize_t rc; unsigned char byte; unsigned short packet_id; size_t payload_len; @@ -462,10 +499,10 @@ static curl_socket_t mqttit(curl_socket_t fd) const size_t client_id_offset = 12; size_t start_usr; size_t start_passwd; + ssize_t rc = 0; /* get the fixed header */ - rc = fixedheader(fd, &byte, &remaining_length, &bytes); - if(rc) + if(!fixedheader(fd, &byte, &remaining_length, &bytes)) break; if(remaining_length >= buff_size) { @@ -666,7 +703,7 @@ static bool mqttd_incoming(curl_socket_t listenfd) } #ifdef HAVE_GETPPID - /* As a last resort, quit if socks5 process becomes orphan. */ + /* As a last resort, quit if mqttd process becomes orphan. */ if(getppid() <= 1) { logmsg("process becomes orphan, exiting"); return FALSE; @@ -675,7 +712,7 @@ static bool mqttd_incoming(curl_socket_t listenfd) do { ssize_t rc; - int error = 0; + int sockerr = 0; char errbuf[STRERROR_LEN]; curl_socket_t sockfd = listenfd; int maxfd = (int)sockfd; @@ -694,20 +731,20 @@ static bool mqttd_incoming(curl_socket_t listenfd) logmsg("signalled to die, exiting..."); return FALSE; } - } while((rc == -1) && ((error = SOCKERRNO) == SOCKEINTR)); + } while((rc == -1) && ((sockerr = SOCKERRNO) == SOCKEINTR)); if(rc < 0) { logmsg("select() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return FALSE; } if(FD_ISSET(sockfd, &fds_read)) { curl_socket_t newfd = accept(sockfd, NULL, NULL); if(newfd == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("accept() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); } else { logmsg("====> Client connect, fd %ld. " @@ -732,7 +769,7 @@ static int test_mqttd(int argc, const char *argv[]) int wrotepidfile = 0; int wroteportfile = 0; bool juggle_again; - int error; + int sockerr; char errbuf[STRERROR_LEN]; int arg = 1; @@ -834,9 +871,9 @@ static int test_mqttd(int argc, const char *argv[]) sock = socket(socket_domain, SOCK_STREAM, 0); if(sock == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto mqttd_cleanup; } diff --git a/tests/server/rtspd.c b/tests/server/rtspd.c index 31820008f0cc..36973185ed7b 100644 --- a/tests/server/rtspd.c +++ b/tests/server/rtspd.c @@ -76,7 +76,7 @@ struct rtspd_httprequest { - skip bytes. */ int rcmd; /* doing a special command, see defines above */ reqprot_t protocol; /* request protocol, HTTP or RTSP */ - int prot_version; /* HTTP or RTSP version (major*10 + minor) */ + int prot_version; /* HTTP or RTSP version (major * 10 + minor) */ bool pipelining; /* true if request is pipelined */ char *rtp_buffer; size_t rtp_buffersize; @@ -96,8 +96,8 @@ struct rtspd_httprequest { #define CMD_AUTH_REQUIRED "auth_required" -/* 'idle' means that it will accept the request fine but never respond - any data. Just keep the connection alive. */ +/* 'idle' means that it accepts the request fine but never responds + any data. Keep the connection alive. */ #define CMD_IDLE "idle" /* 'stream' means to send a never-ending stream of data */ @@ -372,7 +372,7 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) req->testno = DOCNUMBER_BADCONNECT; else if(!strncmp(doc, "test", 4)) { /* if the hostname starts with test, the port number used in the - CONNECT line will be used as test number! */ + CONNECT line is used as test number! */ const char *portp = strchr(doc, ':'); if(portp && (*(portp + 1) != '\0') && ISDIGIT(*(portp + 1))) { pval = portp + 1; @@ -424,7 +424,7 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) if((req->cl == 0) && !CURL_STRNICMP("Content-Length:", line, 15)) { /* If we do not ignore content-length, we read it and we read the whole request including the body before we return. If we have been told to - ignore the content-length, we will return as soon as all headers + ignore the content-length, we return as soon as all headers have been received */ curl_off_t clen; const char *p = line + strlen("Content-Length:"); @@ -438,7 +438,7 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) logmsg("Found Content-Length: %zu in the request", (size_t)clen); if(req->skip) - logmsg("... but will abort after %zu bytes", req->cl); + logmsg("... but going to abort after %zu bytes", req->cl); break; } else if(!CURL_STRNICMP("Transfer-Encoding: chunked", line, @@ -544,67 +544,10 @@ static int rtspd_ProcessRequest(struct rtspd_httprequest *req) return 1; /* done */ } -/* store the entire request in a file */ -static void rtspd_storerequest(const char *reqbuf, size_t totalsize) -{ - int res; - int error = 0; - char errbuf[STRERROR_LEN]; - size_t written; - size_t writeleft; - FILE *dump; - char dumpfile[256]; - - snprintf(dumpfile, sizeof(dumpfile), "%s/%s", logdir, REQUEST_DUMP); - - if(!reqbuf) - return; - if(totalsize == 0) - return; - - do { - dump = curlx_fopen(dumpfile, "ab"); - /* !checksrc! disable ERRNOVAR 1 */ - } while(!dump && ((error = errno) == EINTR)); - if(!dump) { - logmsg("Error opening file %s error (%d) %s", dumpfile, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Failed to write request input to %s", dumpfile); - return; - } - - writeleft = totalsize; - do { - written = fwrite(&reqbuf[totalsize - writeleft], 1, writeleft, dump); - if(got_exit_signal) - goto storerequest_cleanup; - if(written > 0) - writeleft -= written; - error = errno; - /* !checksrc! disable ERRNOVAR 1 */ - } while((writeleft > 0) && (error == EINTR)); - - if(writeleft == 0) - logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile); - else if(writeleft > 0) { - logmsg("Error writing file %s error (%d) %s", dumpfile, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", - totalsize - writeleft, totalsize, dumpfile); - } - -storerequest_cleanup: - - res = curlx_fclose(dump); - if(res) - logmsg("Error closing file %s error (%d) %s", dumpfile, - errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); -} - /* return 0 on success, non-zero on failure */ static int rtspd_get_request(curl_socket_t sock, struct rtspd_httprequest *req) { - int error; + int sockerr; char errbuf[STRERROR_LEN]; int fail = 0; int done_processing = 0; @@ -665,15 +608,15 @@ static int rtspd_get_request(curl_socket_t sock, struct rtspd_httprequest *req) fail = 1; } else if(got < 0) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("recv() returned error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); fail = 1; } if(fail) { /* dump the request received so far to the external file */ reqbuf[req->offset] = '\0'; - rtspd_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP); return 1; } @@ -708,7 +651,8 @@ static int rtspd_get_request(curl_socket_t sock, struct rtspd_httprequest *req) reqbuf[req->offset] = '\0'; /* dump the request to an external file */ - rtspd_storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset); + storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset, + REQUEST_DUMP); if(got_exit_signal) return 1; @@ -730,7 +674,6 @@ static int rtspd_send_doc(curl_socket_t sock, struct rtspd_httprequest *req) size_t responsesize; int error = 0; char errbuf[STRERROR_LEN]; - int res; static char weare[256]; char responsedump[256]; @@ -863,7 +806,7 @@ static int rtspd_send_doc(curl_socket_t sock, struct rtspd_httprequest *req) } /* If the word 'swsclose' is present anywhere in the reply chunk, the - connection will be closed after the data has been sent to the requesting + connection is closed after the data has been sent to the requesting client... */ if(strstr(buffer, "swsclose") || !count) { persistent = FALSE; @@ -890,8 +833,8 @@ static int rtspd_send_doc(curl_socket_t sock, struct rtspd_httprequest *req) responsesize = count; do { - /* Ok, we send no more than 200 bytes at a time, just to make sure that - larger chunks are split up so that the client will need to do multiple + /* Ok, we send no more than 200 bytes at a time, to make sure that + larger chunks are split up so that the client needs to do multiple recv() calls to get it and thus we exercise that code better */ size_t num = count; if(num > 200) @@ -934,8 +877,7 @@ static int rtspd_send_doc(curl_socket_t sock, struct rtspd_httprequest *req) req->rtp_buffersize = 0; } - res = curlx_fclose(dump); - if(res) + if(curlx_fclose(dump)) logmsg("Error closing file %s error (%d) %s", responsedump, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); @@ -968,16 +910,13 @@ static int rtspd_send_doc(curl_socket_t sock, struct rtspd_httprequest *req) if(!strcmp("wait", command)) { logmsg("Told to sleep for %d seconds", num); quarters = num * 4; - while(quarters > 0) { + while((quarters > 0) && !got_exit_signal) { quarters--; - res = curlx_wait_ms(250); - if(got_exit_signal) - break; - if(res) { + if(curlx_wait_ms(250)) { /* should not happen */ - error = SOCKERRNO; + int sockerr = SOCKERRNO; logmsg("curlx_wait_ms() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); break; } } @@ -1014,7 +953,7 @@ static int test_rtspd(int argc, const char *argv[]) unsigned short port = 8999; struct rtspd_httprequest req; int rc; - int error; + int sockerr; char errbuf[STRERROR_LEN]; int arg = 1; @@ -1106,7 +1045,7 @@ static int test_rtspd(int argc, const char *argv[]) snprintf(loglockfile, sizeof(loglockfile), "%s/%s/rtsp-%s.lock", logdir, SERVERLOGS_LOCKDIR, ipv_inuse); - install_signal_handlers(false); + install_signal_handlers(FALSE); #ifdef USE_IPV6 if(!use_ipv6) @@ -1118,17 +1057,17 @@ static int test_rtspd(int argc, const char *argv[]) #endif if(sock == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto server_cleanup; } flag = 1; if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto server_cleanup; } @@ -1151,9 +1090,9 @@ static int test_rtspd(int argc, const char *argv[]) } #endif /* USE_IPV6 */ if(rc) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error binding socket on port %hu (%d) %s", port, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto server_cleanup; } @@ -1172,9 +1111,9 @@ static int test_rtspd(int argc, const char *argv[]) la_size = sizeof(localaddr.sa6); #endif if(getsockname(sock, &localaddr.sa, &la_size) < 0) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); goto server_cleanup; } @@ -1203,17 +1142,16 @@ static int test_rtspd(int argc, const char *argv[]) logmsg("Running %s version on port %d", ipv_inuse, (int)port); /* start accepting connections */ - rc = listen(sock, 5); - if(rc) { - error = SOCKERRNO; + if(listen(sock, 5)) { + sockerr = SOCKERRNO; logmsg("listen() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto server_cleanup; } /* - * As soon as this server writes its pid file the test harness will - * attempt to connect to this server and initiate its verification. + * As soon as this server writes its pid file the test harness attempts + * to connect to this server and initiate its verification. */ wrotepidfile = write_pidfile(pidname); @@ -1232,9 +1170,9 @@ static int test_rtspd(int argc, const char *argv[]) if(got_exit_signal) break; if(msgsock == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("MAJOR ERROR, accept() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); break; } @@ -1343,7 +1281,7 @@ static int test_rtspd(int argc, const char *argv[]) clear_advisor_read_lock(loglockfile); } - restore_signal_handlers(false); + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("========> %s rtspd (port: %d pid: %ld) exits with signal (%d)", diff --git a/tests/server/sockfilt.c b/tests/server/sockfilt.c index 94d632425166..d25195af2d94 100644 --- a/tests/server/sockfilt.c +++ b/tests/server/sockfilt.c @@ -57,25 +57,24 @@ * * This program is intended to be highly portable and as such it must be kept * as simple as possible, due to this the only signal handling mechanisms used - * will be those of ANSI C, and used only in the most basic form which is good + * are those of ANSI C, and used only in the most basic form which is good * enough for the purpose of this program. * * For the above reason and the specific needs of this program signals SIGHUP, - * SIGPIPE and SIGALRM will be ignored on systems where this can be - * done. If possible, signals SIGINT and SIGTERM will be handled by this + * SIGPIPE and SIGALRM are ignored on systems where this can be + * done. If possible, signals SIGINT and SIGTERM are handled by this * program as an indication to cleanup and finish execution as soon as - * possible. This will be achieved with a single signal handler + * possible. This is achieved with a single signal handler * 'exit_signal_handler' for both signals. * * The 'exit_signal_handler' upon the first SIGINT or SIGTERM received signal - * will just set to one the global var 'got_exit_signal' storing in global var + * sets to one the global var 'got_exit_signal' storing in global var * 'exit_signal' the signal that triggered this change. * * Nothing fancy that could introduce problems is used, the program at certain * points in its normal flow checks if var 'got_exit_signal' is set and in - * case this is true it just makes its way out of loops and functions in - * structured and well behaved manner to achieve proper program cleanup and - * termination. + * case this is true it makes its way out of loops and functions in structured + * and well behaved manner to achieve proper program cleanup and termination. * * Even with the above mechanism implemented it is worthwhile to note that * other signals might still be received, or that there might be systems on @@ -172,16 +171,15 @@ static ssize_t write_wincon(int fd, const void *buf, size_t count) #endif /* On Windows, we sometimes get this for a broken pipe, seemingly - * when the client just closed stdin? */ + * when the client closed stdin? */ #define CURL_WIN32_EPIPE 109 /* - * fullread is a wrapper around the read() function. This will repeat the call + * fullread is a wrapper around the read() function. This repeats the call * to read() until it actually has read the complete number of bytes indicated * in nbytes or it fails with a condition that cannot be handled with a simple * retry of the read call. */ - static ssize_t fullread(int filedes, void *buffer, size_t nbytes) { int error; @@ -230,12 +228,11 @@ static ssize_t fullread(int filedes, void *buffer, size_t nbytes) } /* - * fullwrite is a wrapper around the write() function. This will repeat the + * fullwrite is a wrapper around the write() function. This repeats the * call to write() until it actually has written the complete number of bytes * indicated in nbytes or it fails with a condition that cannot be handled * with a simple retry of the write call. */ - static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) { int error; @@ -280,11 +277,10 @@ static ssize_t fullwrite(int filedes, const void *buffer, size_t nbytes) /* * read_stdin tries to read from stdin nbytes into the given buffer. This is a - * blocking function that will only return TRUE when nbytes have actually been + * blocking function that only returns TRUE when nbytes have actually been * read or FALSE when an unrecoverable error has been detected. Failure of this * function is an indication that the sockfilt process should terminate. */ - static bool read_stdin(void *buffer, size_t nbytes) { ssize_t nread = fullread(fileno(stdin), buffer, nbytes); @@ -297,11 +293,10 @@ static bool read_stdin(void *buffer, size_t nbytes) /* * write_stdout tries to write to stdio nbytes from the given buffer. This is a - * blocking function that will only return TRUE when nbytes have actually been + * blocking function that only returns TRUE when nbytes have actually been * written or FALSE when an unrecoverable error has been detected. Failure of * this function is an indication that the sockfilt process should terminate. */ - static bool write_stdout(const void *buffer, size_t nbytes) { ssize_t nwrite; @@ -436,7 +431,7 @@ static DWORD WINAPI select_ws_wait_thread(void *lpParameter) switch(type) { case FILE_TYPE_DISK: /* The handle represents a file on disk, this means: - * - WaitForMultipleObjectsEx will always be signalled for it. + * - WaitForMultipleObjectsEx is always signalled for it. * - comparison of current position in file and total size of * the file can be used to check if we reached the end yet. * @@ -473,7 +468,7 @@ static DWORD WINAPI select_ws_wait_thread(void *lpParameter) case FILE_TYPE_CHAR: /* The handle represents a character input, this means: - * - WaitForMultipleObjectsEx will be signalled on any kind of input, + * - WaitForMultipleObjectsEx is signalled on any kind of input, * including mouse and window size events we do not care about. * * Approach: Loop till either the internal event is signalled @@ -502,7 +497,7 @@ static DWORD WINAPI select_ws_wait_thread(void *lpParameter) case FILE_TYPE_PIPE: /* The handle represents an anonymous or named pipe, this means: - * - WaitForMultipleObjectsEx will always be signalled for it. + * - WaitForMultipleObjectsEx is always signalled for it. * - peek into the pipe and retrieve the amount of data available. * * Approach: Loop till either the internal event is signalled @@ -897,7 +892,7 @@ static bool disc_handshake(void) return FALSE; } else if(!memcmp("QUIT", buffer, 4)) { - /* just die */ + /* die */ logmsg("quits"); return FALSE; } @@ -933,7 +928,7 @@ static bool juggle(curl_socket_t *sockfdp, curl_socket_t sockfd = CURL_SOCKET_BAD; int maxfd = -99; ssize_t rc; - int error = 0; + int sockerr = 0; char errbuf[STRERROR_LEN]; unsigned char buffer[BUFFER_SIZE]; @@ -945,7 +940,7 @@ static bool juggle(curl_socket_t *sockfdp, } #ifdef HAVE_GETPPID - /* As a last resort, quit if sockfilt process becomes orphan. Just in case + /* As a last resort, quit if sockfilt process becomes orphan. In case parent ftpserver process has died without killing its sockfilt children */ if(getppid() <= 1) { logmsg("process becomes orphan, exiting"); @@ -1019,11 +1014,11 @@ static bool juggle(curl_socket_t *sockfdp, logmsg("signalled to die, exiting..."); return FALSE; } - } while((rc == -1) && ((error = SOCKERRNO) == SOCKEINTR)); + } while((rc == -1) && ((sockerr = SOCKERRNO) == SOCKEINTR)); if(rc < 0) { logmsg("select() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return FALSE; } @@ -1055,7 +1050,7 @@ static bool juggle(curl_socket_t *sockfdp, buffer[0], buffer[1], buffer[2], buffer[3]); if(!memcmp("PING", buffer, 4)) { - /* send reply on stdout, just proving we are alive */ + /* send reply on stdout, proving we are alive */ if(!write_stdout("PONG\n", 5)) return FALSE; } @@ -1073,7 +1068,7 @@ static bool juggle(curl_socket_t *sockfdp, return FALSE; } else if(!memcmp("QUIT", buffer, 4)) { - /* just die */ + /* die */ logmsg("quits"); return FALSE; } @@ -1125,9 +1120,9 @@ static bool juggle(curl_socket_t *sockfdp, client connecting. */ curl_socket_t newfd = accept(sockfd, NULL, NULL); if(newfd == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("accept() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); } else { logmsg("====> Client connect"); @@ -1179,7 +1174,7 @@ static int test_sockfilt(int argc, const char *argv[]) int wroteportfile = 0; bool juggle_again; int rc; - int error; + int sockerr; char errbuf[STRERROR_LEN]; int arg = 1; enum sockmode mode = PASSIVE_LISTEN; /* default */ @@ -1293,14 +1288,14 @@ static int test_sockfilt(int argc, const char *argv[]) CURL_BINMODE(stdout); CURL_BINMODE(stderr); - install_signal_handlers(false); + install_signal_handlers(FALSE); sock = socket(socket_domain, SOCK_STREAM, 0); if(sock == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); write_stdout("FAIL\n", 5); goto sockfilt_cleanup; } @@ -1336,9 +1331,9 @@ static int test_sockfilt(int argc, const char *argv[]) rc = 1; } if(rc) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error connecting to port %hu (%d) %s", server_connectport, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); write_stdout("FAIL\n", 5); goto sockfilt_cleanup; } @@ -1394,7 +1389,7 @@ static int test_sockfilt(int argc, const char *argv[]) if(wroteportfile) unlink(portname); - restore_signal_handlers(false); + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("============> sockfilt exits with signal (%d)", exit_signal); diff --git a/tests/server/socksd.c b/tests/server/socksd.c index 8a4840ce69b4..9e1caedc2d34 100644 --- a/tests/server/socksd.c +++ b/tests/server/socksd.c @@ -44,7 +44,7 @@ * "password [string]" - the password that must match (if method is 2) * "backend [IPv4]" - numerical IPv4 address of backend to connect to * "backendport [number:0]" - TCP port of backend to connect to. 0 means use - the client's specified port number. + * the client's specified port number. * "method [number: 0]" - connect method to respond with: * 0 - no auth * 1 - GSSAPI (not supported) @@ -164,9 +164,9 @@ static void socksd_getconfig(void) logmsg("password [%s] set", s_config.password); } /* Methods: - o X'00' NO AUTHENTICATION REQUIRED - o X'01' GSSAPI - o X'02' USERNAME/PASSWORD + o 0x00 NO AUTHENTICATION REQUIRED + o 0x01 GSSAPI + o 0x02 USERNAME/PASSWORD */ else if(!strcmp(key, "method")) { pval = value; @@ -213,7 +213,6 @@ static void socksd_getconfig(void) static curl_socket_t socksconnect(unsigned short connectport, const char *connectaddr) { - int rc; srvr_sockaddr_union_t me; curl_socket_t sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == CURL_SOCKET_BAD) @@ -224,13 +223,11 @@ static curl_socket_t socksconnect(unsigned short connectport, me.sa4.sin_addr.s_addr = INADDR_ANY; curlx_inet_pton(AF_INET, connectaddr, &me.sa4.sin_addr); - rc = connect(sock, &me.sa, sizeof(me.sa4)); - - if(rc) { + if(connect(sock, &me.sa, sizeof(me.sa4))) { char errbuf[STRERROR_LEN]; - int error = SOCKERRNO; + int sockerr = SOCKERRNO; logmsg("Failed connecting to %s:%hu (%d) %s", connectaddr, connectport, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return CURL_SOCKET_BAD; } logmsg("Connected fine to %s:%d", connectaddr, connectport); @@ -441,9 +438,9 @@ static curl_socket_t sockit(curl_socket_t fd) return CURL_SOCKET_BAD; } /* ATYP: - o IP V4 address: X'01' - o DOMAINNAME: X'03' - o IP V6 address: X'04' + o IPv4 address: 0x01 + o domain name: 0x03 + o IPv6 address: 0x04 */ type = buffer[SOCKS5_ATYP]; address = &buffer[SOCKS5_DSTADDR]; @@ -522,17 +519,17 @@ static curl_socket_t sockit(curl_socket_t fd) response[SOCKS5_VERSION] = s_config.responseversion; /* - o REP Reply field: - o X'00' succeeded - o X'01' general SOCKS server failure - o X'02' connection not allowed by ruleset - o X'03' Network unreachable - o X'04' Host unreachable - o X'05' Connection refused - o X'06' TTL expired - o X'07' Command not supported - o X'08' Address type not supported - o X'09' to X'FF' unassigned + o REP Reply field: + o 0x00 succeeded + o 0x01 general SOCKS server failure + o 0x02 connection not allowed by ruleset + o 0x03 Network unreachable + o 0x04 Host unreachable + o 0x05 Connection refused + o 0x06 TTL expired + o 0x07 Command not supported + o 0x08 Address type not supported + o 0x09 to 0xFF unassigned */ response[SOCKS5_REP] = rep; response[SOCKS5_RESERVED] = 0; /* must be zero */ @@ -636,7 +633,7 @@ static bool socksd_incoming(curl_socket_t listenfd) do { int i; ssize_t rc; - int error = 0; + int sockerr = 0; char errbuf[STRERROR_LEN]; curl_socket_t sockfd = listenfd; int maxfd = (int)sockfd; @@ -668,20 +665,20 @@ static bool socksd_incoming(curl_socket_t listenfd) logmsg("signalled to die, exiting..."); return FALSE; } - } while((rc == -1) && ((error = SOCKERRNO) == SOCKEINTR)); + } while((rc == -1) && ((sockerr = SOCKERRNO) == SOCKEINTR)); if(rc < 0) { logmsg("select() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return FALSE; } if((clients < 2) && FD_ISSET(sockfd, &fds_read)) { curl_socket_t newfd = accept(sockfd, NULL, NULL); if(newfd == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("accept() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); } else { curl_socket_t remotefd; @@ -732,13 +729,12 @@ static int test_socksd(int argc, const char *argv[]) int wrotepidfile = 0; int wroteportfile = 0; bool juggle_again; - int error; char errbuf[STRERROR_LEN]; int arg = 1; const char *unix_socket = NULL; #ifdef USE_UNIX_SOCKETS - bool unlink_socket = false; + bool unlink_socket = FALSE; #endif pidname = ".socksd.pid"; @@ -861,14 +857,14 @@ static int test_socksd(int argc, const char *argv[]) CURL_BINMODE(stdout); CURL_BINMODE(stderr); - install_signal_handlers(false); + install_signal_handlers(FALSE); sock = socket(socket_domain, SOCK_STREAM, 0); if(sock == CURL_SOCKET_BAD) { - error = SOCKERRNO; + int sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto socks5_cleanup; } @@ -879,7 +875,7 @@ static int test_socksd(int argc, const char *argv[]) goto socks5_cleanup; } #ifdef USE_UNIX_SOCKETS - unlink_socket = true; + unlink_socket = TRUE; #endif msgsock = CURL_SOCKET_BAD; /* no stream socket yet */ } @@ -918,11 +914,10 @@ static int test_socksd(int argc, const char *argv[]) sclose(sock); #ifdef USE_UNIX_SOCKETS - if(unlink_socket && socket_domain == AF_UNIX && unix_socket) { - error = unlink(unix_socket); - logmsg("unlink(%s) = %d (%s)", unix_socket, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - } + if(unlink_socket && socket_domain == AF_UNIX && unix_socket && + unlink(unix_socket)) + logmsg("unlink(%s): %d (%s)", unix_socket, + errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif if(wrotepidfile) @@ -930,7 +925,7 @@ static int test_socksd(int argc, const char *argv[]) if(wroteportfile) unlink(portname); - restore_signal_handlers(false); + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("============> socksd exits with signal (%d)", exit_signal); diff --git a/tests/server/sws.c b/tests/server/sws.c index c2f62384b8df..04e6025fa5fe 100644 --- a/tests/server/sws.c +++ b/tests/server/sws.c @@ -100,6 +100,9 @@ static size_t num_sockets = 0; #define REQUEST_PROXY_DUMP "proxy.input" #define RESPONSE_PROXY_DUMP "proxy.response" +#define REQUEST_DUMP_FILENAME \ + (is_proxy ? REQUEST_PROXY_DUMP : REQUEST_DUMP) + /* file in which additional instructions may be found */ static const char *cmdfile = "log/server.cmd"; @@ -112,14 +115,14 @@ static const char *cmdfile = "log/server.cmd"; #define CMD_AUTH_REQUIRED "auth_required" -/* 'idle' means that it will accept the request fine but never respond - any data. Just keep the connection alive. */ +/* 'idle' means that it accepts the request fine but never responds + any data. Keep the connection alive. */ #define CMD_IDLE "idle" /* 'stream' means to send a never-ending stream of data */ #define CMD_STREAM "stream" -/* 'connection-monitor' will output when a server/proxy connection gets +/* 'connection-monitor' outputs when a server/proxy connection gets disconnected as for some cases it is important that it gets done at the proper point - like with NTLM */ #define CMD_CONNECTIONMONITOR "connection-monitor" @@ -195,10 +198,10 @@ static bool socket_domain_is_ip(void) #ifdef USE_IPV6 case AF_INET6: #endif - return true; + return TRUE; default: /* case AF_UNIX: */ - return false; + return FALSE; } } #endif @@ -304,7 +307,7 @@ static int sws_parse_servercmd(struct sws_httprequest *req) else { logmsg("Unknown instruction found: %s", cmd); } - /* try to deal with CRLF or just LF */ + /* try to deal with CRLF or LF */ check = strchr(cmd, '\r'); if(!check) check = strchr(cmd, '\n'); @@ -585,7 +588,7 @@ static int sws_ProcessRequest(struct sws_httprequest *req) if((req->cl == 0) && !CURL_STRNICMP("Content-Length:", line, 15)) { /* If we do not ignore content-length, we read it and we read the whole request including the body before we return. If we have been told to - ignore the content-length, we will return as soon as all headers + ignore the content-length, we return as soon as all headers have been received */ curl_off_t clen; const char *p = line + strlen("Content-Length:"); @@ -602,7 +605,7 @@ static int sws_ProcessRequest(struct sws_httprequest *req) logmsg("Found Content-Length: %zu in the request", (size_t)clen); if(req->skip) - logmsg("... but will abort after %zu bytes", req->cl); + logmsg("... but going to abort after %zu bytes", req->cl); } else if(!CURL_STRNICMP("Transfer-Encoding: chunked", line, strlen("Transfer-Encoding: chunked"))) { @@ -739,64 +742,6 @@ static int sws_ProcessRequest(struct sws_httprequest *req) return 1; /* done */ } -/* store the entire request in a file */ -static void sws_storerequest(const char *reqbuf, size_t totalsize) -{ - int res; - int error = 0; - char errbuf[STRERROR_LEN]; - size_t written; - size_t writeleft; - FILE *dump; - char dumpfile[256]; - - snprintf(dumpfile, sizeof(dumpfile), "%s/%s", - logdir, is_proxy ? REQUEST_PROXY_DUMP : REQUEST_DUMP); - - if(!reqbuf) - return; - if(totalsize == 0) - return; - - do { - dump = curlx_fopen(dumpfile, "ab"); - /* !checksrc! disable ERRNOVAR 1 */ - } while(!dump && ((error = errno) == EINTR)); - if(!dump) { - logmsg("[2] Error opening file %s error (%d) %s", dumpfile, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Failed to write request input "); - return; - } - - writeleft = totalsize; - do { - written = fwrite(&reqbuf[totalsize - writeleft], 1, writeleft, dump); - if(got_exit_signal) - goto storerequest_cleanup; - if(written > 0) - writeleft -= written; - error = errno; - /* !checksrc! disable ERRNOVAR 1 */ - } while((writeleft > 0) && (error == EINTR)); - - if(writeleft == 0) - logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile); - else if(writeleft > 0) { - logmsg("Error writing file %s error (%d) %s", dumpfile, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); - logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", - totalsize - writeleft, totalsize, dumpfile); - } - -storerequest_cleanup: - - res = curlx_fclose(dump); - if(res) - logmsg("Error closing file %s error (%d) %s", dumpfile, - errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); -} - /* returns -1 on failure */ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) { @@ -813,7 +758,6 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) size_t responsesize; int error = 0; char errbuf[STRERROR_LEN]; - int res; static char weare[256]; char responsedump[256]; @@ -940,7 +884,7 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) } /* If the word 'swsclose' is present anywhere in the reply chunk, the - connection will be closed after the data has been sent to the requesting + connection is closed after the data has been sent to the requesting client... */ if(strstr(buffer, "swsclose") || !count || req->close) { persistent = FALSE; @@ -966,8 +910,8 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) responsesize = count; do { - /* Ok, we send no more than N bytes at a time, just to make sure that - larger chunks are split up so that the client will need to do multiple + /* Ok, we send no more than N bytes at a time, to make sure that + larger chunks are split up so that the client needs to do multiple recv() calls to get it and thus we exercise that code better */ size_t num = count; if(num > 20) @@ -976,7 +920,7 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) retry: written = swrite(sock, buffer, num); if(written < 0) { - if((SOCKEWOULDBLOCK == SOCKERRNO) || (EAGAIN == SOCKERRNO)) { + if(SOCK_EAGAIN(SOCKERRNO)) { curlx_wait_ms(10); goto retry; } @@ -1007,8 +951,7 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) } } while((count > 0) && !got_exit_signal); - res = curlx_fclose(dump); - if(res) + if(curlx_fclose(dump)) logmsg("Error closing file %s error (%d) %s", responsedump, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); @@ -1045,12 +988,11 @@ static int sws_send_doc(curl_socket_t sock, struct sws_httprequest *req) quarters = num * 4; while((quarters > 0) && !got_exit_signal) { quarters--; - res = curlx_wait_ms(250); - if(res) { + if(curlx_wait_ms(250)) { /* should not happen */ - error = SOCKERRNO; + int sockerr = SOCKERRNO; logmsg("curlx_wait_ms() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); break; } } @@ -1119,7 +1061,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) /* dump the request received so far to the external file */ reqbuf[req->offset] = '\0'; - sws_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP_FILENAME); req->offset = 0; /* read websocket traffic */ @@ -1133,8 +1075,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) logmsg("Got %zd bytes from client", got); } - if((got == -1) && - ((SOCKERRNO == EAGAIN) || (SOCKERRNO == SOCKEWOULDBLOCK))) { + if((got == -1) && SOCK_EAGAIN(SOCKERRNO)) { int rc; fd_set input; fd_set output; @@ -1163,7 +1104,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) logmsg("log the websocket traffic"); /* dump the incoming websocket traffic to the external file */ reqbuf[req->offset] = '\0'; - sws_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP_FILENAME); req->offset = 0; } init_httprequest(req); @@ -1193,19 +1134,19 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) } else if(got < 0) { char errbuf[STRERROR_LEN]; - int error = SOCKERRNO; - if(EAGAIN == error || SOCKEWOULDBLOCK == error) { + int sockerr = SOCKERRNO; + if(SOCK_EAGAIN(sockerr)) { /* nothing to read at the moment */ return 0; } logmsg("recv() returned error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); fail = 1; } if(fail) { /* dump the request received so far to the external file */ reqbuf[req->offset] = '\0'; - sws_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP_FILENAME); return -1; } @@ -1236,7 +1177,7 @@ static int sws_get_request(curl_socket_t sock, struct sws_httprequest *req) /* at the end of a request dump it to an external file */ if(fail || req->done_processing) - sws_storerequest(reqbuf, req->offset); + storerequest(reqbuf, req->offset, REQUEST_DUMP_FILENAME); if(got_exit_signal) return -1; @@ -1247,7 +1188,7 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) { srvr_sockaddr_union_t serveraddr; curl_socket_t serverfd; - int error; + int sockerr; char errbuf[STRERROR_LEN]; int rc = 0; const char *op_br = ""; @@ -1267,9 +1208,9 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) serverfd = socket(socket_domain, SOCK_STREAM, 0); if(serverfd == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error creating socket for server connection (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return CURL_SOCKET_BAD; } @@ -1287,9 +1228,9 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) * Windows has an internal retry logic that may lead to long * timeouts if the peer is not listening. */ if(curlx_nonblock(serverfd, TRUE)) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("curlx_nonblock(TRUE) failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(serverfd); return CURL_SOCKET_BAD; } @@ -1334,8 +1275,8 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) } if(rc) { - error = SOCKERRNO; - if((error == SOCKEINPROGRESS) || (error == SOCKEWOULDBLOCK)) { + sockerr = SOCKERRNO; + if((sockerr == SOCKEINPROGRESS) || SOCK_EAGAIN(sockerr)) { fd_set output; struct timeval timeout = { 0 }; timeout.tv_sec = 1; /* 1000 ms */ @@ -1344,16 +1285,17 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) FD_SET(serverfd, &output); while(1) { rc = select((int)serverfd + 1, NULL, &output, NULL, &timeout); - if(rc < 0 && SOCKERRNO != SOCKEINTR) + sockerr = SOCKERRNO; + if(rc < 0 && sockerr != SOCKEINTR) goto error; else if(rc > 0) { - curl_socklen_t errSize = sizeof(error); + curl_socklen_t errSize = sizeof(sockerr); if(getsockopt(serverfd, SOL_SOCKET, SO_ERROR, - (void *)&error, &errSize)) - error = SOCKERRNO; - if((error == 0) || (SOCKEISCONN == error)) + (void *)&sockerr, &errSize)) + sockerr = SOCKERRNO; + if((sockerr == 0) || (SOCKEISCONN == sockerr)) goto success; - else if((error != SOCKEINPROGRESS) && (error != SOCKEWOULDBLOCK)) + else if((sockerr != SOCKEINPROGRESS) && !SOCK_EAGAIN(sockerr)) goto error; } else if(!rc) { @@ -1365,7 +1307,7 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) } error: logmsg("Error connecting to server port %hu (%d) %s", port, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(serverfd); return CURL_SOCKET_BAD; } @@ -1374,9 +1316,9 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) op_br, ipaddr, cl_br, port); if(curlx_nonblock(serverfd, FALSE)) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("curlx_nonblock(FALSE) failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(serverfd); return CURL_SOCKET_BAD; } @@ -1392,7 +1334,7 @@ static curl_socket_t connect_to(const char *ipaddr, unsigned short port) * either end. * * When doing FTP through a CONNECT proxy, we expect that the data connection - * will be setup while the first connect is still being kept up. Therefore we + * is setup while the first connect is still being kept up. Therefore we * must accept a new connection and deal with it appropriately. */ @@ -1794,7 +1736,7 @@ static void http_connect(curl_socket_t *infdp, static void http_upgrade(struct sws_httprequest *req) { (void)req; - logmsg("Upgraded to ... %u", req->upgrade_request); + logmsg("Upgraded to ... %d", (int)req->upgrade_request); /* left to implement */ } @@ -1803,7 +1745,7 @@ static void http_upgrade(struct sws_httprequest *req) static curl_socket_t accept_connection(curl_socket_t sock) { curl_socket_t msgsock = CURL_SOCKET_BAD; - int error; + int sockerr; char errbuf[STRERROR_LEN]; int flag = 1; @@ -1821,20 +1763,20 @@ static curl_socket_t accept_connection(curl_socket_t sock) } if(msgsock == CURL_SOCKET_BAD) { - error = SOCKERRNO; - if(EAGAIN == error || SOCKEWOULDBLOCK == error) { + sockerr = SOCKERRNO; + if(SOCK_EAGAIN(sockerr)) { /* nothing to accept */ return 0; } logmsg("MAJOR ERROR, accept() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return CURL_SOCKET_BAD; } if(curlx_nonblock(msgsock, TRUE)) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("curlx_nonblock failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(msgsock); return CURL_SOCKET_BAD; } @@ -1844,9 +1786,9 @@ static curl_socket_t accept_connection(curl_socket_t sock) #endif if(setsockopt(msgsock, SOL_SOCKET, SO_KEEPALIVE, (void *)&flag, sizeof(flag))) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("setsockopt(SO_KEEPALIVE) failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(msgsock); return CURL_SOCKET_BAD; } @@ -1971,11 +1913,11 @@ static int test_sws(int argc, const char *argv[]) unsigned short port = 8999; #ifdef USE_UNIX_SOCKETS const char *unix_socket = NULL; - bool unlink_socket = false; + bool unlink_socket = FALSE; #endif struct sws_httprequest *req = NULL; int rc = 0; - int error; + int sockerr; char errbuf[STRERROR_LEN]; int arg = 1; const char *connecthost = "127.0.0.1"; @@ -2102,7 +2044,7 @@ static int test_sws(int argc, const char *argv[]) } } else if(!strcmp("--connect", argv[arg])) { - /* The connect host IP number that the proxy will connect to no matter + /* The connect host IP number that the proxy connects to no matter what the client asks for, but also use this as a hint that we run as a proxy and do a few different internal choices */ arg++; @@ -2135,7 +2077,7 @@ static int test_sws(int argc, const char *argv[]) logdir, SERVERLOGS_LOCKDIR, protocol_type, is_proxy ? "-proxy" : "", socket_type); - install_signal_handlers(false); + install_signal_handlers(FALSE); req = calloc(1, sizeof(*req)); if(!req) @@ -2147,9 +2089,9 @@ static int test_sws(int argc, const char *argv[]) num_sockets = 1; if(sock == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto sws_cleanup; } @@ -2159,18 +2101,18 @@ static int test_sws(int argc, const char *argv[]) flag = 1; if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto sws_cleanup; } #if defined(_WIN32) && defined(USE_UNIX_SOCKETS) } #endif if(curlx_nonblock(sock, TRUE)) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("curlx_nonblock failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto sws_cleanup; } @@ -2197,15 +2139,15 @@ static int test_sws(int argc, const char *argv[]) #endif /* USE_UNIX_SOCKETS */ } if(rc) { - error = SOCKERRNO; + sockerr = SOCKERRNO; #ifdef USE_UNIX_SOCKETS if(socket_domain == AF_UNIX) logmsg("Error binding socket on path %s (%d) %s", unix_socket, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); else #endif logmsg("Error binding socket on port %hu (%d) %s", port, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto sws_cleanup; } @@ -2224,9 +2166,9 @@ static int test_sws(int argc, const char *argv[]) la_size = sizeof(localaddr.sa6); #endif if(getsockname(sock, &localaddr.sa, &la_size) < 0) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); goto sws_cleanup; } @@ -2261,22 +2203,21 @@ static int test_sws(int argc, const char *argv[]) protocol_type, socket_type, location_str); /* start accepting connections */ - rc = listen(sock, 50); - if(rc) { - error = SOCKERRNO; + if(listen(sock, 50)) { + sockerr = SOCKERRNO; logmsg("listen() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto sws_cleanup; } #ifdef USE_UNIX_SOCKETS /* listen succeeds, so let's assume a valid listening Unix socket */ - unlink_socket = true; + unlink_socket = TRUE; #endif /* - * As soon as this server writes its pid file the test harness will - * attempt to connect to this server and initiate its verification. + * As soon as this server writes its pid file the test harness attempts + * to connect to this server and initiate its verification. */ wrotepidfile = write_pidfile(pidname); @@ -2329,17 +2270,18 @@ static int test_sws(int argc, const char *argv[]) if(got_exit_signal) goto sws_cleanup; + sockerr = 0; do { rc = select((int)maxfd + 1, &input, &output, NULL, &timeout); - } while(rc < 0 && SOCKERRNO == SOCKEINTR && !got_exit_signal); + } while(rc < 0 && ((sockerr = SOCKERRNO) == SOCKEINTR && + !got_exit_signal)); if(got_exit_signal) goto sws_cleanup; if(rc < 0) { - error = SOCKERRNO; logmsg("select() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); goto sws_cleanup; } @@ -2384,7 +2326,7 @@ static int test_sws(int argc, const char *argv[]) if(req->connmon) { const char *keepopen = "[DISCONNECT]\n"; - sws_storerequest(keepopen, strlen(keepopen)); + storerequest(keepopen, strlen(keepopen), REQUEST_DUMP_FILENAME); req->connmon = FALSE; } @@ -2415,12 +2357,12 @@ static int test_sws(int argc, const char *argv[]) * 2) the socket is still open, and * 3) (stale) data is still available (or about to be available) * on that socket - * In that case, this loop will run once more and treat that stale + * In that case, this loop runs once more and treat that stale * data (in service_connection()) as the first data received on * this new HTTP request and report "** Unusual request" (skipall * would have otherwise caused that data to be ignored). Normally, - * that socket will be closed by the client and there will not be - * any stale data to cause this, but stranger things have happened + * that socket is closed by the client and there is no stale data + * to cause this, but stranger things have happened * (see issue #11678). */ init_httprequest(req); @@ -2443,11 +2385,10 @@ static int test_sws(int argc, const char *argv[]) sclose(sock); #ifdef USE_UNIX_SOCKETS - if(unlink_socket && socket_domain == AF_UNIX && unix_socket) { - rc = unlink(unix_socket); - logmsg("unlink(%s) = %d (%s)", unix_socket, - rc, curlx_strerror(rc, errbuf, sizeof(errbuf))); - } + if(unlink_socket && socket_domain == AF_UNIX && unix_socket && + unlink(unix_socket)) + logmsg("unlink(%s): %d (%s)", unix_socket, + errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); #endif free(req); @@ -2465,7 +2406,7 @@ static int test_sws(int argc, const char *argv[]) clear_advisor_read_lock(loglockfile); } - restore_signal_handlers(false); + restore_signal_handlers(FALSE); if(got_exit_signal) { logmsg("========> %s sws (%s pid: %ld) exits with signal (%d)", diff --git a/tests/server/tftpd.c b/tests/server/tftpd.c index 44e7976f1cd4..f730506649bc 100644 --- a/tests/server/tftpd.c +++ b/tests/server/tftpd.c @@ -413,7 +413,7 @@ static ssize_t write_behind(struct testcase *test, int convert) b = &bfs[nextone]; if(b->counter < -1) /* anything to flush? */ - return 0; /* just nop if nothing to do */ + return 0; /* nop if nothing to do */ if(!test->ofile) { char outfile[256]; @@ -453,11 +453,11 @@ static ssize_t write_behind(struct testcase *test, int convert) while(ct--) { /* loop over the buffer */ c = (unsigned char)*p++; /* pick up a character */ if(prevchar == '\r') { /* if prev char was cr */ - if(c == '\n') /* if have cr,lf then just */ + if(c == '\n') /* if have cr,lf then */ curl_lseek(test->ofile, -1, SEEK_CUR); /* smash lf on top of the cr */ else if(c == '\0') /* if have cr,nul then */ - goto skipit; /* just skip over the putc */ - /* else just fall through and allow it */ + goto skipit; /* skip over the putc */ + /* else fall through and allow it */ } /* formerly putc(c, file); */ @@ -488,7 +488,7 @@ static int writeit(struct testcase *test, struct tftphdr * volatile *dpp, * synch. Ie: that what I think is the other side's response to packet N is * really their response to packet N-1. * - * So, to try to prevent that, we flush all the input queued up for us on the + * To try to prevent that, we flush all the input queued up for us on the * network connection on our host. * * We return the number of packets we flushed (mostly for reporting when trace @@ -570,7 +570,7 @@ static int tftpd_parse_servercmd(struct testcase *req) else { logmsg("Unknown instruction found: %s", cmd); } - /* try to deal with CRLF or just LF */ + /* try to deal with CRLF or LF */ check = strchr(cmd, '\r'); if(!check) check = strchr(cmd, '\n'); @@ -858,7 +858,7 @@ static void recvtftp(struct testcase *test, const struct formats *pf) rap->th_block = htons(recvblock); (void)swrite(peer, &ackbuf.storage[0], 4); #if defined(HAVE_ALARM) && defined(SIGALRM) - mysignal(SIGALRM, justtimeout); /* just abort read on timeout */ + mysignal(SIGALRM, justtimeout); /* abort read on timeout */ alarm(rexmtval); #endif /* normally times out and quits */ @@ -918,10 +918,10 @@ static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size) cp = (char *)&tp->th_stuff; filename = cp; do { - bool endofit = true; + bool endofit = TRUE; while(cp < &trsbuf.storage[size]) { if(*cp == '\0') { - endofit = false; + endofit = FALSE; break; } cp++; @@ -970,7 +970,7 @@ static int do_tftp(struct testcase *test, struct tftphdr *tp, ssize_t size) curlx_fclose(server); for(pf = formata; pf->f_mode; pf++) - if(strcmp(pf->f_mode, mode) == 0) + if(!strcmp(pf->f_mode, mode)) break; if(!pf->f_mode) { nak(TFTP_EBADOP); @@ -1015,7 +1015,7 @@ static int test_tftpd(int argc, const char **argv) curl_socket_t sock = CURL_SOCKET_BAD; int flag; int rc; - int error; + int sockerr; char errbuf[STRERROR_LEN]; struct testcase test; int result = 0; @@ -1109,7 +1109,7 @@ static int test_tftpd(int argc, const char **argv) snprintf(loglockfile, sizeof(loglockfile), "%s/%s/tftp-%s.lock", logdir, SERVERLOGS_LOCKDIR, ipv_inuse); - install_signal_handlers(true); + install_signal_handlers(TRUE); #ifdef USE_IPV6 if(!use_ipv6) @@ -1121,18 +1121,18 @@ static int test_tftpd(int argc, const char **argv) #endif if(sock == CURL_SOCKET_BAD) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error creating socket (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto tftpd_cleanup; } flag = 1; if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag))) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto tftpd_cleanup; } @@ -1156,9 +1156,9 @@ static int test_tftpd(int argc, const char **argv) } #endif /* USE_IPV6 */ if(rc) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("Error binding socket on port %hu (%d) %s", port, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); result = 1; goto tftpd_cleanup; } @@ -1178,9 +1178,9 @@ static int test_tftpd(int argc, const char **argv) la_size = sizeof(localaddr.sa6); #endif if(getsockname(sock, &localaddr.sa, &la_size) < 0) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); goto tftpd_cleanup; } @@ -1327,7 +1327,7 @@ static int test_tftpd(int argc, const char **argv) clear_advisor_read_lock(loglockfile); } - restore_signal_handlers(true); + restore_signal_handlers(TRUE); if(got_exit_signal) { logmsg("========> %s tftpd (port: %d pid: %ld) exits with signal (%d)", diff --git a/tests/server/util.c b/tests/server/util.c index 3a76d2769755..77d635a9e907 100644 --- a/tests/server/util.c +++ b/tests/server/util.c @@ -42,7 +42,7 @@ void loghex(const unsigned char *buffer, ssize_t len) ssize_t width = 0; int left = sizeof(data); - for(i = 0; i < len && (left >= 0); i++) { + for(i = 0; i < len && (left > 2); i++) { snprintf(optr, left, "%02x", ptr[i]); width += 2; optr += 2; @@ -66,7 +66,7 @@ void logmsg(const char *msg, ...) static int known_offset; if(!serverlogfile) { - fprintf(stderr, "Serverlogfile not set error\n"); + fprintf(stderr, "Error: Server log file not set\n"); return; } @@ -139,7 +139,7 @@ int win32_init(void) if(err) { curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)); fprintf(stderr, "Winsock init failed: %s\n", buffer); - logmsg("Error initialising Winsock -- aborting"); + logmsg("Error initializing Winsock -- aborting"); return 1; } @@ -230,7 +230,6 @@ void set_advisor_read_lock(const char *filename) FILE *lockfile; int error = 0; char errbuf[STRERROR_LEN]; - int res; do { lockfile = curlx_fopen(filename, "wb"); @@ -242,8 +241,7 @@ void set_advisor_read_lock(const char *filename) return; } - res = curlx_fclose(lockfile); - if(res) + if(curlx_fclose(lockfile)) logmsg("Error closing lock file %s error (%d) %s", filename, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); } @@ -251,7 +249,7 @@ void set_advisor_read_lock(const char *filename) void clear_advisor_read_lock(const char *filename) { int error = 0; - int res; + int rc; /* * Log all removal failures. Even those due to file not existing. @@ -260,16 +258,70 @@ void clear_advisor_read_lock(const char *filename) */ do { - res = unlink(filename); + rc = unlink(filename); /* !checksrc! disable ERRNOVAR 1 */ - } while(res && ((error = errno) == EINTR)); - if(res) { + } while(rc && ((error = errno) == EINTR)); + if(rc) { char errbuf[STRERROR_LEN]; logmsg("Error removing lock file %s error (%d) %s", filename, error, curlx_strerror(error, errbuf, sizeof(errbuf))); } } +/* store the entire request in a file */ +void storerequest(const char *reqbuf, size_t totalsize, const char *filename) +{ + int error = 0; + char errbuf[STRERROR_LEN]; + size_t written; + size_t writeleft; + FILE *dump; + char dumpfile[256]; + + snprintf(dumpfile, sizeof(dumpfile), "%s/%s", logdir, filename); + + if(!reqbuf) + return; + if(totalsize == 0) + return; + + do { + dump = curlx_fopen(dumpfile, "ab"); + /* !checksrc! disable ERRNOVAR 1 */ + } while(!dump && ((error = errno) == EINTR)); + if(!dump) { + logmsg("storerequest: Error opening file %s error (%d) %s", dumpfile, + error, curlx_strerror(error, errbuf, sizeof(errbuf))); + return; + } + + writeleft = totalsize; + do { + written = fwrite(&reqbuf[totalsize - writeleft], 1, writeleft, dump); + if(got_exit_signal) + goto storerequest_cleanup; + if(written > 0) + writeleft -= written; + error = errno; + /* !checksrc! disable ERRNOVAR 1 */ + } while((writeleft > 0) && (error == EINTR)); + + if(writeleft == 0) + logmsg("Wrote request (%zu bytes) input to %s", totalsize, dumpfile); + else if(writeleft > 0) { + logmsg("Error writing file %s error (%d) %s", dumpfile, + error, curlx_strerror(error, errbuf, sizeof(errbuf))); + logmsg("Wrote only (%zu bytes) of (%zu bytes) request input to %s", + totalsize - writeleft, totalsize, dumpfile); + } + +storerequest_cleanup: + + if(curlx_fclose(dump)) + logmsg("Error closing file %s error (%d) %s", dumpfile, + errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); +} + /* vars used to keep around previous signal handlers */ typedef void (*SIGHANDLER_T)(int); @@ -311,24 +363,29 @@ static HANDLE thread_main_window = NULL; static HWND hidden_main_window = NULL; #endif -/* signal handler that will be triggered to indicate that the program +/* signal handler that is triggered to indicate that the program * should finish its execution in a controlled manner as soon as possible. - * The first time this is called it will set got_exit_signal to one and - * store in exit_signal the signal that triggered its execution. - */ -/* + * The first time this is called it sets got_exit_signal to 1 and + * stores in exit_signal the signal that triggered its execution. + * * Only call signal-safe functions from the signal handler, as required by * the POSIX specification: * https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html * Hence, do not call 'logmsg()', and instead use 'open/write/close' to * log errors. */ +/* suppress warnings seen in configurations where 'write()' has the attribute + 'warn_unused_result', which is not silenced by casting to '(void)'. */ +#if defined(CURL_HAVE_DIAG) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" /* GCC 4.5+ */ +#endif static void exit_signal_handler(int signum) { int old_errno = errno; if(!serverlogfile) { static const char msg[] = "exit_signal_handler: serverlogfile not set\n"; - (void)!write(STDERR_FILENO, msg, sizeof(msg) - 1); + (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); } else { int fd = -1; @@ -342,14 +399,14 @@ static void exit_signal_handler(int signum) if(fd != -1) { #endif static const char msg[] = "exit_signal_handler: called\n"; - (void)!write(fd, msg, sizeof(msg) - 1); + (void)write(fd, msg, sizeof(msg) - 1); curlx_close(fd); } else { static const char msg[] = "exit_signal_handler: failed opening "; - (void)!write(STDERR_FILENO, msg, sizeof(msg) - 1); - (void)!write(STDERR_FILENO, serverlogfile, strlen(serverlogfile)); - (void)!write(STDERR_FILENO, "\n", 1); + (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); + (void)write(STDERR_FILENO, serverlogfile, strlen(serverlogfile)); + (void)write(STDERR_FILENO, "\n", 1); } } if(got_exit_signal == 0) { @@ -363,6 +420,9 @@ static void exit_signal_handler(int signum) (void)signal(signum, exit_signal_handler); errno = old_errno; } +#if defined(CURL_HAVE_DIAG) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif #ifdef _WIN32 /* CTRL event handler for Windows Console applications to simulate @@ -656,7 +716,6 @@ void restore_signal_handlers(bool keep_sigalrm) int bind_unix_socket(curl_socket_t sock, const char *unix_socket, struct sockaddr_un *sau) { - int error; char errbuf[STRERROR_LEN]; int rc; size_t len; @@ -677,47 +736,45 @@ int bind_unix_socket(curl_socket_t sock, const char *unix_socket, curlx_strcopy(sau->sun_path, sizeof(sau->sun_path), unix_socket, len); rc = bind(sock, (struct sockaddr *)sau, sizeof(struct sockaddr_un)); if(rc && SOCKERRNO == SOCKEADDRINUSE) { - curlx_struct_stat statbuf; + int sockerr; /* socket already exists. Perhaps it is stale? */ curl_socket_t unixfd = socket(AF_UNIX, SOCK_STREAM, 0); if(unixfd == CURL_SOCKET_BAD) { + sockerr = SOCKERRNO; logmsg("Failed to create socket at %s (%d) %s", unix_socket, - SOCKERRNO, curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return -1; } /* check whether the server is alive */ rc = connect(unixfd, (struct sockaddr*)sau, sizeof(struct sockaddr_un)); - error = SOCKERRNO; + sockerr = SOCKERRNO; sclose(unixfd); - if(rc && error != SOCKECONNREFUSED) { + if(rc && sockerr != SOCKECONNREFUSED) { logmsg("Failed to connect to %s (%d) %s", unix_socket, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); return rc; } +#if !defined(_WIN32) && defined(S_IFSOCK) /* No lstat(), S_IFSOCK on Windows */ /* socket server is not alive, now check if it was actually a socket. */ -#ifdef _WIN32 - /* Windows does not have lstat function. */ - rc = curlx_stat(unix_socket, &statbuf); -#else - rc = lstat(unix_socket, &statbuf); -#endif - if(rc) { - logmsg("Error binding socket, failed to stat %s (%d) %s", unix_socket, - errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); - return rc; - } -#ifdef S_IFSOCK - if((statbuf.st_mode & S_IFSOCK) != S_IFSOCK) { - logmsg("Error binding socket, failed to stat %s", unix_socket); - return -1; + { + curlx_struct_stat statbuf; + rc = lstat(unix_socket, &statbuf); + if(rc && errno != ENOENT) { + logmsg("Error binding socket, failed to stat %s (%d) %s", unix_socket, + errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); + return -1; + } + if(!rc && (statbuf.st_mode & S_IFMT) != S_IFSOCK) { + logmsg("Error binding socket, %s is not a socket", unix_socket); + return -1; + } } #endif /* dead socket, cleanup and retry bind */ - rc = unlink(unix_socket); - if(rc) { - logmsg("Error binding socket, failed to unlink %s (%d) %s", unix_socket, + if(unlink(unix_socket) && errno != ENOENT) { + logmsg("Error binding socket, failed to unlink %s: %d (%s)", unix_socket, errno, curlx_strerror(errno, errbuf, sizeof(errbuf))); - return rc; + return -1; } /* stale socket is gone, retry bind */ rc = bind(sock, (struct sockaddr *)sau, sizeof(struct sockaddr_un)); @@ -739,7 +796,7 @@ curl_socket_t sockdaemon(curl_socket_t sock, int maxretr = 10; int delay = 20; int attempt = 0; - int error = 0; + int sockerr = 0; char errbuf[STRERROR_LEN]; #ifndef USE_UNIX_SOCKETS @@ -755,16 +812,15 @@ curl_socket_t sockdaemon(curl_socket_t sock, rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flag, sizeof(flag)); if(rc) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); if(maxretr) { - rc = curlx_wait_ms(delay); - if(rc) { + if(curlx_wait_ms(delay)) { /* should not happen */ - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("curlx_wait_ms() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); return CURL_SOCKET_BAD; } @@ -782,7 +838,7 @@ curl_socket_t sockdaemon(curl_socket_t sock, if(rc) { logmsg("setsockopt(SO_REUSEADDR) failed %d times in %d ms. " "Error (%d) %s", attempt, totdelay, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); logmsg("Continuing anyway..."); } #if defined(_WIN32) && defined(USE_UNIX_SOCKETS) @@ -819,15 +875,15 @@ curl_socket_t sockdaemon(curl_socket_t sock, } if(rc) { - error = SOCKERRNO; + sockerr = SOCKERRNO; #ifdef USE_UNIX_SOCKETS if(socket_domain == AF_UNIX) logmsg("Error binding socket on path %s (%d) %s", unix_socket, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); else #endif logmsg("Error binding socket on port %hu (%d) %s", *listenport, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); return CURL_SOCKET_BAD; } @@ -849,9 +905,9 @@ curl_socket_t sockdaemon(curl_socket_t sock, #endif la_size = sizeof(localaddr.sa4); if(getsockname(sock, &localaddr.sa, &la_size) < 0) { - error = SOCKERRNO; + sockerr = SOCKERRNO; logmsg("getsockname() failed with error (%d) %s", - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); return CURL_SOCKET_BAD; } @@ -885,11 +941,10 @@ curl_socket_t sockdaemon(curl_socket_t sock, } /* start accepting connections */ - rc = listen(sock, 5); - if(rc) { - error = SOCKERRNO; + if(listen(sock, 5)) { + sockerr = SOCKERRNO; logmsg("listen(%ld, 5) failed with error (%d) %s", (long)sock, - error, curlx_strerror(error, errbuf, sizeof(errbuf))); + sockerr, curlx_strerror(sockerr, errbuf, sizeof(errbuf))); sclose(sock); return CURL_SOCKET_BAD; } diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm index 3f345ad74a44..66c971b49953 100644 --- a/tests/serverhelp.pm +++ b/tests/serverhelp.pm @@ -67,7 +67,7 @@ use testutil qw( our $logfile; # server log filename, for logmsg #*************************************************************************** -# Just for convenience, test harness uses 'https' and 'httptls' literals as +# For convenience, test harness uses 'https' and 'httptls' literals as # values for 'proto' variable in order to differentiate different servers. # 'https' literal is used for stunnel based https test servers, and 'httptls' # is used for non-stunnel https test servers. @@ -77,8 +77,7 @@ our $logfile; # server log filename, for logmsg # sub logmsg { my ($seconds, $usec) = Time::HiRes::gettimeofday(); - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = - localtime($seconds); + my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($seconds); my $now = sprintf("%02d:%02d:%02d.%06d ", $hour, $min, $sec, $usec); # we see warnings on Windows run that $logfile is used uninitialized # TODO: not found yet where this comes from diff --git a/tests/servers.pm b/tests/servers.pm index dba3c3e1d1e1..474a12c377e7 100644 --- a/tests/servers.pm +++ b/tests/servers.pm @@ -119,32 +119,32 @@ use testutil qw( my %serverpidfile; # all server pid filenames, identified by server id my %serverportfile;# all server port filenames, identified by server id -my $sshdvernum; # for socks server, ssh daemon version number -my $sshdverstr; # for socks server, ssh daemon version string -my $sshderror; # for socks server, ssh daemon version error -my %doesntrun; # servers that do not work, identified by pidfile +my $sshdvernum; # for socks server, ssh daemon version number +my $sshdverstr; # for socks server, ssh daemon version string +my $sshderror; # for socks server, ssh daemon version error +my %doesntrun; # servers that do not work, identified by pidfile my %PORT = (nolisten => 47); # port we use for a local non-listening service -my $server_response_maxtime=13; +my $server_response_maxtime = 13; my $httptlssrv = find_httptlssrv(); -my %run; # running server -my %runcert; # cert file currently in use by an ssl running server -my $CLIENTIP="127.0.0.1"; # address which curl uses for incoming connections -my $CLIENT6IP="[::1]"; # address which curl uses for incoming connections +my %run; # running server +my %runcert; # cert file currently in use by an SSL running server +my $CLIENTIP = "127.0.0.1"; # address which curl uses for incoming connections +my $CLIENT6IP = "[::1]"; # address which curl uses for incoming connections my $posix_pwd = build_sys_abs_path($pwd); # current working directory in POSIX format -my $h2cver = "h2c"; # this version is decided by the nghttp2 lib being used -my $HOSTIP="127.0.0.1"; # address on which the test server listens -my $HOST6IP="[::1]"; # address on which the test server listens -my $HTTPUNIXPATH; # HTTP server Unix domain socket path -my $SOCKSUNIXPATH; # socks server Unix domain socket path +my $h2cver = "h2c"; # this version is decided by the nghttp2 lib being used +my $HOSTIP = "127.0.0.1"; # address on which the test server listens +my $HOST6IP = "[::1]"; # address on which the test server listens +my $HTTPUNIXPATH; # HTTP server Unix domain socket path +my $SOCKSUNIXPATH; # socks server Unix domain socket path my $SSHSRVMD5 = "[uninitialized]"; # MD5 of ssh server public key my $SSHSRVSHA256 = "[uninitialized]"; # SHA256 of ssh server public key -my $USER; # name of the current user -my $sshdid; # for socks server, ssh daemon version id -my $ftpchecktime=1; # time it took to verify our test FTP server +my $USER; # name of the current user +my $sshdid; # for socks server, ssh daemon version id +my $ftpchecktime = 1; # time it took to verify our test FTP server my $SERVER_TIMEOUT_SEC = 15; # time for a server to spin up # Variables shared with runtests.pl -our $SOCKSIN="socksd-request.log"; # what curl sent to the SOCKS proxy +our $SOCKSIN = "socksd-request.log"; # what curl sent to the SOCKS proxy our $err_unexpected; # error instead of warning on server unexpectedly alive our $debugprotocol; # nonzero for verbose server logs our $stunnel; # path to stunnel command @@ -153,15 +153,15 @@ our $stunnel; # path to stunnel command # Check for a command in the PATH of the test server. # sub checkcmd { - my ($cmd, @extrapaths)=@_; + my ($cmd, @extrapaths) = @_; my @paths; if($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') { # PATH separator is different - @paths=(split(';', $ENV{'PATH'}), @extrapaths); + @paths = (split(';', $ENV{'PATH'}), @extrapaths); } else { - @paths=(split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", - "/sbin", "/usr/bin", "/usr/local/bin", @extrapaths); + @paths = (split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin", + "/sbin", "/usr/bin", "/usr/local/bin", @extrapaths); } for(@paths) { if(-x "$_/$cmd" . exe_ext('SYS') && ! -d "$_/$cmd" . exe_ext('SYS')) { @@ -206,11 +206,11 @@ sub initserverconfig { $USER = $ENV{LOGNAME}; # Some Unix (I think) } if(!$USER) { - $USER = `whoami`; + $USER = qx(whoami); chomp $USER; } if(!$USER) { - $USER = `id -un`; + $USER = qx(id -un); chomp $USER; } init_serverpidfile_hash(); @@ -265,7 +265,7 @@ sub init_serverpidfile_hash { } ####################################################################### -# Check if a given child process has just died. Reaps it if so. +# Check if a given child process has died. Reaps it if so. # sub checkdied { my $pid = $_[0]; @@ -291,7 +291,7 @@ sub checkdied { # 4 for an unsupported server type # sub serverfortest { - my (@what)=@_; + my (@what) = @_; for(my $i = scalar(@what) - 1; $i >= 0; $i--) { my $srvrline = $what[$i]; @@ -332,7 +332,7 @@ sub serverfortest { # Return the pids (yes plural) of the new child process to the parent. # sub startnew { - my ($cmd, $pidfile, $timeout, $fakepidfile)=@_; + my ($cmd, $pidfile, $timeout, $fakepidfile) = @_; logmsg "startnew: $cmd\n" if($verbose); @@ -351,10 +351,10 @@ sub startnew { # Put an "exec" in front of the command so that the child process # keeps this child's process ID. - exec("exec $cmd") || die "Cannot exec() $cmd: $!"; + exec("exec $cmd") or die "Cannot exec() $cmd: $!"; # exec() should never return back here to this process. We protect - # ourselves by calling die() just in case something goes really bad. + # ourselves by calling die() in case something goes really bad. die "error: exec() has returned"; } @@ -362,7 +362,7 @@ sub startnew { if($fakepidfile) { if(open(my $out, ">", $pidfile)) { print $out $child . "\n"; - close($out) || die "Failure writing pidfile"; + close($out) or die "Failure writing pidfile"; logmsg "startnew: $pidfile faked with pid=$child\n" if($verbose); } else { @@ -382,17 +382,17 @@ sub startnew { $pid2 = pidfromfile($pidfile, 0); if(($pid2 > 0) && pidexists($pid2)) { # if $pid2 is valid, then make sure this pid is alive, as - # otherwise it is just likely to be the _previous_ pidfile or + # otherwise it is likely to be the _previous_ pidfile or # similar! last; } if(checkdied($child)) { logmsg "startnew: child process has died, server might start up\n" if($verbose); - # We cannot just abort waiting for the server with a + # We cannot abort waiting for the server with a # return (-1,-1); # because the server might have forked and could still start - # up normally. Instead, just reduce the amount of time we remain + # up normally. Instead, reduce the amount of time we remain # waiting. $count >>= 2; } @@ -435,11 +435,11 @@ sub stopserver { # my @killservers; if($server =~ /^(ftp|http|imap|pop3|smtp)s((\d*)(-ipv6|-unix|))$/) { - # given a stunnel based ssl server, also kill non-ssl underlying one + # given a stunnel based SSL server, also kill non-SSL underlying one push @killservers, "${1}${2}"; } elsif($server =~ /^(ftp|http|imap|pop3|smtp)((\d*)(-ipv6|-unix|))$/) { - # given a non-ssl server, also kill stunnel based ssl piggybacking one + # given a non-SSL server, also kill stunnel based SSL piggybacking one push @killservers, "${1}s${2}"; } elsif($server =~ /^(socks)((\d*)(-ipv6|))$/) { @@ -515,7 +515,7 @@ sub getexternalproxyflags { sub verifyhttp { my ($proto, $ipvnum, $idnum, $ip, $port_or_path, $do_http3) = @_; my $server = servername_id($proto, $ipvnum, $idnum); - my $bonus=""; + my $bonus = ""; # $port_or_path contains a path for Unix sockets, sws ignores the port my $port = ($ipvnum eq "unix") ? 80 : $port_or_path; my $infix = ($do_http3) ? "_h3" : ""; @@ -530,7 +530,7 @@ sub verifyhttp { if($proto eq "gopher") { # gopher is funny - $bonus="1/"; + $bonus = "1/"; } my $flags = "--max-time $server_response_maxtime "; @@ -602,8 +602,8 @@ sub verifyhttp { sub verifyftp { my ($proto, $ipvnum, $idnum, $ip, $port) = @_; my $server = servername_id($proto, $ipvnum, $idnum); - my $time=time(); - my $extra=""; + my $time = time(); + my $extra = ""; my $verifylog = "$LOGDIR/". servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; @@ -894,8 +894,8 @@ sub verifypid { sub verifysmb { my ($proto, $ipvnum, $idnum, $ip, $port) = @_; my $server = servername_id($proto, $ipvnum, $idnum); - my $time=time(); - my $extra=""; + my $time = time(); + my $extra = ""; my $verifylog = "$LOGDIR/". servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; @@ -954,8 +954,8 @@ sub verifysmb { sub verifytelnet { my ($proto, $ipvnum, $idnum, $ip, $port) = @_; my $server = servername_id($proto, $ipvnum, $idnum); - my $time=time(); - my $extra=""; + my $time = time(); + my $extra = ""; my $verifylog = "$LOGDIR/". servername_canon($proto, $ipvnum, $idnum) .'_verify.log'; @@ -1012,7 +1012,7 @@ sub verifytelnet { # particular can take a long time to start if it needs to generate # keys on a slow or loaded host. # -# Just for convenience, test harness uses 'https' and 'httptls' literals +# For convenience, test harness uses 'https' and 'httptls' literals # as values for 'proto' variable in order to differentiate different # servers. 'https' literal is used for stunnel based https test servers, # and 'httptls' is used for non-stunnel https test servers. @@ -1164,7 +1164,7 @@ sub runhttpserver { # sub runhttp2server { my ($verb) = @_; - my $proto="http/2"; + my $proto = "http/2"; my $ipvnum = 4; my $idnum = 0; my $exe = "$perl " . shell_quote("$srcdir/http2-server.pl"); @@ -1225,7 +1225,7 @@ sub runhttp2server { # sub runhttp3server { my ($verb, $cert) = @_; - my $proto="http/3"; + my $proto = "http/3"; my $ipvnum = 4; my $idnum = 0; my $exe = "$perl " . shell_quote("$srcdir/http3-server.pl"); @@ -1348,7 +1348,7 @@ sub runhttpsserver { if($httpspid <= 0 || !pidexists($httpspid)) { # it is NOT alive - # do not call stopserver since that will also kill the dependent + # do not call stopserver since that also kills the dependent # server that has already been started properly $doesntrun{$pidfile} = 1; $httpspid = $pid2 = 0; @@ -1552,7 +1552,7 @@ sub runsecureserver { if($protospid <= 0 || !pidexists($protospid)) { # it is NOT alive - # do not call stopserver since that will also kill the dependent + # do not call stopserver since that also kills the dependent # server that has already been started properly $doesntrun{$pidfile} = 1; $protospid = $pid2 = 0; @@ -1676,7 +1676,7 @@ sub rundnsserver { my $portfile = $serverportfile{$server}; my $logfile = server_logfilename($LOGDIR, $proto, $ipvnum, $idnum); - my $cmd=server_exe('dnsd'); + my $cmd = server_exe('dnsd'); $cmd .= " --port 0"; $cmd .= " --verbose" if($debugprotocol); $cmd .= " --pidfile \"$pidfile\""; @@ -1800,7 +1800,7 @@ sub sshkeyalgostr { # sub runsshserver { my ($id, $verb, $ipv6) = @_; - my $ip=$HOSTIP; + my $ip = $HOSTIP; my $proto = 'ssh'; my $ipvnum = 4; my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; @@ -1925,7 +1925,7 @@ sub runsshserver { # sub runmqttserver { my ($id, $verb, $ipv6) = @_; - my $ip=$HOSTIP; + my $ip = $HOSTIP; my $proto = 'mqtt'; my $port = protoport($proto); my $ipvnum = 4; @@ -1951,7 +1951,7 @@ sub runmqttserver { unlink($portfile); # need to see a new one # start our MQTT server - on a random port! - my $cmd=server_exe('mqttd'). + my $cmd = server_exe('mqttd'). " --port 0". " --pidfile $pidfile". " --portfile $portfile". @@ -1988,7 +1988,7 @@ sub runmqttserver { # sub runsocksserver { my ($id, $verb, $ipv6, $is_unix) = @_; - my $ip=$HOSTIP; + my $ip = $HOSTIP; my $proto = 'socks'; my $ipvnum = 4; my $idnum = ($id && ($id =~ /^(\d+)$/) && ($id > 1)) ? $id : 1; @@ -2014,9 +2014,9 @@ sub runsocksserver { unlink($portfile); # need to see a new one # start our socks server, get commands from the FTP cmd file - my $cmd=""; + my $cmd = ""; if($is_unix) { - $cmd=server_exe('socksd'). + $cmd = server_exe('socksd'). " --pidfile $pidfile". " --reqfile $LOGDIR/$SOCKSIN". " --logfile $logfile". @@ -2025,7 +2025,7 @@ sub runsocksserver { " --config $LOGDIR/$SERVERCMD"; $portfile = "none"; } else { - $cmd=server_exe('socksd'). + $cmd = server_exe('socksd'). " --port 0". " --pidfile $pidfile". " --portfile $portfile". @@ -2433,7 +2433,7 @@ sub startservers { return ("failed starting ". uc($what) ." server", $serr); } logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2) if($verbose); - $run{$what}="$pid $pid2"; + $run{$what} = "$pid $pid2"; } } elsif($what eq "ftp-ipv6") { @@ -2450,7 +2450,7 @@ sub startservers { } logmsg sprintf("* pid ftp-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'ftp-ipv6'}="$pid $pid2"; + $run{'ftp-ipv6'} = "$pid $pid2"; } } elsif($what eq "gopher") { @@ -2469,7 +2469,7 @@ sub startservers { } logmsg sprintf ("* pid gopher => %d %d\n", $pid, $pid2) if($verbose); - $run{'gopher'}="$pid $pid2"; + $run{'gopher'} = "$pid $pid2"; } } elsif($what eq "gopher-ipv6") { @@ -2488,7 +2488,7 @@ sub startservers { } logmsg sprintf("* pid gopher-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'gopher-ipv6'}="$pid $pid2"; + $run{'gopher-ipv6'} = "$pid $pid2"; } } elsif($what eq "http") { @@ -2507,7 +2507,7 @@ sub startservers { } logmsg sprintf ("* pid http => %d %d\n", $pid, $pid2) if($verbose); - $run{'http'}="$pid $pid2"; + $run{'http'} = "$pid $pid2"; } } elsif($what eq "http-proxy") { @@ -2526,7 +2526,7 @@ sub startservers { } logmsg sprintf ("* pid http-proxy => %d %d\n", $pid, $pid2) if($verbose); - $run{'http-proxy'}="$pid $pid2"; + $run{'http-proxy'} = "$pid $pid2"; } } elsif($what eq "http-ipv6") { @@ -2545,7 +2545,7 @@ sub startservers { } logmsg sprintf("* pid http-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'http-ipv6'}="$pid $pid2"; + $run{'http-ipv6'} = "$pid $pid2"; } } elsif($what eq "rtsp") { @@ -2561,7 +2561,7 @@ sub startservers { return ("failed starting RTSP server", $serr); } logmsg sprintf("* pid rtsp => %d %d\n", $pid, $pid2) if($verbose); - $run{'rtsp'}="$pid $pid2"; + $run{'rtsp'} = "$pid $pid2"; } } elsif($what eq "rtsp-ipv6") { @@ -2578,7 +2578,7 @@ sub startservers { } logmsg sprintf("* pid rtsp-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'rtsp-ipv6'}="$pid $pid2"; + $run{'rtsp-ipv6'} = "$pid $pid2"; } } elsif($what =~ /^(ftp|imap|pop3|smtp)s$/) { @@ -2605,7 +2605,7 @@ sub startservers { return ("failed starting $cproto server", $serr); } logmsg sprintf("* pid $cproto => %d %d\n", $pid, $pid2) if($verbose); - $run{$cproto}="$pid $pid2"; + $run{$cproto} = "$pid $pid2"; } if(!$run{$what}) { ($serr, $pid, $pid2, $PORT{$what}) = @@ -2616,7 +2616,7 @@ sub startservers { } logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2) if($verbose); - $run{$what}="$pid $pid2"; + $run{$what} = "$pid $pid2"; } } elsif($what eq "file") { @@ -2663,7 +2663,7 @@ sub startservers { return ("failed starting HTTP server (for https/https-mtls)", $serr); } logmsg sprintf("* pid http => %d %d\n", $pid, $pid2) if($verbose); - $run{'http'}="$pid $pid2"; + $run{'http'} = "$pid $pid2"; } if(!$run{$what}) { ($serr, $pid, $pid2, $PORT{$what}) = @@ -2673,7 +2673,7 @@ sub startservers { } logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2) if($verbose); - $run{$what}="$pid $pid2"; + $run{$what} = "$pid $pid2"; } } elsif($what eq "http/2") { @@ -2704,7 +2704,7 @@ sub startservers { return ("failed starting HTTP server (for http/2)", $serr); } logmsg sprintf("* pid http => %d %d\n", $pid, $pid2) if($verbose); - $run{'http'}="$pid $pid2"; + $run{'http'} = "$pid $pid2"; } if(!$run{'http/2'}) { ($serr, $pid, $pid2, $PORT{"http2"}, $PORT{"http2tls"}) = @@ -2714,7 +2714,7 @@ sub startservers { } logmsg sprintf ("* pid http/2 => %d %d\n", $pid, $pid2) if($verbose); - $run{'http/2'}="$pid $pid2"; + $run{'http/2'} = "$pid $pid2"; } } elsif($what eq "http/3") { @@ -2745,7 +2745,7 @@ sub startservers { return ("failed starting HTTP server (for http/3)", $serr); } logmsg sprintf("* pid http => %d %d\n", $pid, $pid2) if($verbose); - $run{'http'}="$pid $pid2"; + $run{'http'} = "$pid $pid2"; } if(!$run{'http/3'}) { ($serr, $pid, $pid2, $PORT{"http3"}) = runhttp3server($verbose); @@ -2754,7 +2754,7 @@ sub startservers { } logmsg sprintf ("* pid http/3 => %d %d\n", $pid, $pid2) if($verbose); - $run{'http/3'}="$pid $pid2"; + $run{'http/3'} = "$pid $pid2"; } } elsif($what eq "gophers") { @@ -2777,15 +2777,14 @@ sub startservers { } if(!$run{'gopher'}) { my $port; - ($serr, $pid, $pid2, $port) = - runhttpserver("gopher", $verbose, 0); + ($serr, $pid, $pid2, $port) = runhttpserver("gopher", $verbose, 0); $PORT{'gopher'} = $port; if($pid <= 0) { return ("failed starting GOPHER server", $serr); } logmsg sprintf("* pid gopher => %d %d\n", $pid, $pid2) if($verbose); logmsg "GOPHERPORT => $port\n" if($verbose); - $run{'gopher'}="$pid $pid2"; + $run{'gopher'} = "$pid $pid2"; } if(!$run{'gophers'}) { my $port; @@ -2798,7 +2797,7 @@ sub startservers { logmsg sprintf("* pid gophers => %d %d\n", $pid, $pid2) if($verbose); logmsg "GOPHERSPORT => $port\n" if($verbose); - $run{'gophers'}="$pid $pid2"; + $run{'gophers'} = "$pid $pid2"; } } elsif($what eq "https-proxy") { @@ -2829,7 +2828,7 @@ sub startservers { } logmsg sprintf("* pid https-proxy => %d %d\n", $pid, $pid2) if($verbose); - $run{'https-proxy'}="$pid $pid2"; + $run{'https-proxy'} = "$pid $pid2"; } } elsif($what eq "httptls") { @@ -2851,7 +2850,7 @@ sub startservers { } logmsg sprintf("* pid httptls => %d %d\n", $pid, $pid2) if($verbose); - $run{'httptls'}="$pid $pid2"; + $run{'httptls'} = "$pid $pid2"; } } elsif($what eq "httptls-ipv6") { @@ -2873,7 +2872,7 @@ sub startservers { } logmsg sprintf("* pid httptls-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'httptls-ipv6'}="$pid $pid2"; + $run{'httptls-ipv6'} = "$pid $pid2"; } } elsif($what eq "dns") { @@ -2890,7 +2889,7 @@ sub startservers { return ("failed starting DNS server", $serr); } logmsg sprintf("* pid dns => %d %d\n", $pid, $pid2) if($verbose); - $run{'dns'}="$pid $pid2"; + $run{'dns'} = "$pid $pid2"; } } elsif($what eq "tftp") { @@ -2907,7 +2906,7 @@ sub startservers { return ("failed starting TFTP server", $serr); } logmsg sprintf("* pid tftp => %d %d\n", $pid, $pid2) if($verbose); - $run{'tftp'}="$pid $pid2"; + $run{'tftp'} = "$pid $pid2"; } } elsif($what eq "tftp-ipv6") { @@ -2924,7 +2923,7 @@ sub startservers { return ("failed starting TFTP-IPv6 server", $serr); } logmsg sprintf("* pid tftp-ipv6 => %d %d\n", $pid, $pid2) if($verbose); - $run{'tftp-ipv6'}="$pid $pid2"; + $run{'tftp-ipv6'} = "$pid $pid2"; } } elsif($what eq "sftp" || $what eq "scp") { @@ -2934,7 +2933,7 @@ sub startservers { return ("failed starting SSH server", $serr); } logmsg sprintf("* pid ssh => %d %d\n", $pid, $pid2) if($verbose); - $run{'ssh'}="$pid $pid2"; + $run{'ssh'} = "$pid $pid2"; } } elsif($what eq "socks4" || $what eq "socks5" ) { @@ -2944,7 +2943,7 @@ sub startservers { return ("failed starting socks server", $serr); } logmsg sprintf("* pid socks => %d %d\n", $pid, $pid2) if($verbose); - $run{'socks'}="$pid $pid2"; + $run{'socks'} = "$pid $pid2"; } } elsif($what eq "socks5unix") { @@ -2954,7 +2953,7 @@ sub startservers { return ("failed starting socks5unix server", $serr); } logmsg sprintf("* pid socks5unix => %d %d\n", $pid, $pid2) if($verbose); - $run{'socks5unix'}="$pid $pid2"; + $run{'socks5unix'} = "$pid $pid2"; } } elsif($what eq "mqtt" ) { @@ -2970,7 +2969,7 @@ sub startservers { return ("failed starting mqtt server", $serr); } logmsg sprintf("* pid mqtt => %d %d\n", $pid, $pid2) if($verbose); - $run{'mqtt'}="$pid $pid2"; + $run{'mqtt'} = "$pid $pid2"; } } elsif($what eq "mqtts" ) { @@ -2990,7 +2989,7 @@ sub startservers { return ("failed starting mqtt server", $serr); } logmsg sprintf("* pid mqtt => %d %d\n", $pid, $pid2) if($verbose); - $run{'mqtt'}="$pid $pid2"; + $run{'mqtt'} = "$pid $pid2"; } if(!$run{$what}) { ($serr, $pid, $pid2, $PORT{$what}) = @@ -3000,7 +2999,7 @@ sub startservers { } logmsg sprintf("* pid $what => %d %d\n", $pid, $pid2) if($verbose); - $run{$what}="$pid $pid2"; + $run{$what} = "$pid $pid2"; } } elsif($what eq "http-unix") { @@ -3019,7 +3018,7 @@ sub startservers { } logmsg sprintf("* pid http-unix => %d %d\n", $pid, $pid2) if($verbose); - $run{'http-unix'}="$pid $pid2"; + $run{'http-unix'} = "$pid $pid2"; } } elsif($what eq "dict") { @@ -3030,7 +3029,7 @@ sub startservers { } logmsg sprintf ("* pid DICT => %d %d\n", $pid, $pid2) if($verbose); - $run{'dict'}="$pid $pid2"; + $run{'dict'} = "$pid $pid2"; } } elsif($what eq "smb") { @@ -3041,7 +3040,7 @@ sub startservers { } logmsg sprintf ("* pid SMB => %d %d\n", $pid, $pid2) if($verbose); - $run{'smb'}="$pid $pid2"; + $run{'smb'} = "$pid $pid2"; } } elsif($what eq "telnet") { @@ -3053,7 +3052,7 @@ sub startservers { } logmsg sprintf ("* pid neg TELNET => %d %d\n", $pid, $pid2) if($verbose); - $run{'telnet'}="$pid $pid2"; + $run{'telnet'} = "$pid $pid2"; } } else { diff --git a/tests/smbserver.py b/tests/smbserver.py index c2942079882e..44daefc15a11 100755 --- a/tests/smbserver.py +++ b/tests/smbserver.py @@ -25,6 +25,7 @@ import argparse import configparser +import importlib.util import logging import os import signal @@ -36,9 +37,7 @@ from util import ClosingFileHandler, TestData # impacket needs to be installed in the Python environment -try: - import impacket # noqa: F401 -except ImportError: +if importlib.util.find_spec('impacket') is None: sys.stderr.write( 'Warning: Python package impacket is required for smb testing; ' 'use pip or your package manager to install it\n') @@ -76,7 +75,7 @@ def __enter__(self): signal.signal(signal.SIGTERM, self._sighandler) def __exit__(self, *_): - # Call for shutdown just in case it was not done already + # Call for shutdown in case it was not done already self.shutdown_event.set() # Wait for thread, and therefore also the server, to finish self.join() @@ -125,7 +124,7 @@ def smbserver(options): smb_config.set("SERVER", "share type", "0") smb_config.set("SERVER", "path", SERVER_MAGIC) - # Have a share for tests. These files will be autogenerated from the + # Have a share for tests. These files are auto-generated from the # test input. smb_config.add_section("TESTS") smb_config.set("TESTS", "comment", "tests") @@ -146,7 +145,7 @@ def smbserver(options): # Start a thread that cleanly shuts down the server on a signal with ShutdownHandler(smb_server): - # This will block until smb_server.shutdown() is called + # This blocks until smb_server.shutdown() is called smb_server.serve_forever() return 0 @@ -199,8 +198,7 @@ def create_and_x(self, conn_id, smb_server, smb_command, recv_packet): # Currently we only support reading files. if disposition != imp_smb.FILE_OPEN: - raise SmbError(STATUS_ACCESS_DENIED, - "Only support reading files") + raise SmbError(STATUS_ACCESS_DENIED, "Only support reading files") # Check to see if the path we were given is actually a # magic path which needs generating on the fly. @@ -213,8 +211,7 @@ def create_and_x(self, conn_id, smb_server, smb_command, recv_packet): flags2 = recv_packet["Flags2"] ncax_data = imp_smb.SMBNtCreateAndX_Data(flags=flags2, - data=smb_command[ - "Data"]) + data=smb_command["Data"]) requested_file = imp_smbserver.decodeSMBString( flags2, ncax_data["FileName"]) @@ -235,7 +232,7 @@ def create_and_x(self, conn_id, smb_server, smb_command, recv_packet): if len(conn_data["OpenedFiles"]) == 0: fakefid = 1 else: - fakefid = conn_data["OpenedFiles"].keys()[-1] + 1 + fakefid = max(conn_data["OpenedFiles"].keys()) + 1 resp_params["Fid"] = fakefid resp_params["CreateAction"] = disposition @@ -294,16 +291,14 @@ def get_share_path(self, conn_data, root_fid, tid): if root_fid > 0: # If we have a rootFid, the path is relative to that fid path = conn_data["OpenedFiles"][root_fid]["FileName"] - log.debug("RootFid present %s!" % path) + log.debug(f'RootFid present {path}!') else: if "path" in conn_shares[tid]: path = conn_shares[tid]["path"] else: - raise SmbError(STATUS_ACCESS_DENIED, - "Connection share had no path") + raise SmbError(STATUS_ACCESS_DENIED, "Connection share had no path") else: - raise SmbError(imp_smbserver.STATUS_SMB_BAD_TID, - "TID was invalid") + raise SmbError(imp_smbserver.STATUS_SMB_BAD_TID, "TID was invalid") return path @@ -317,7 +312,7 @@ def get_server_path(self, requested_filename): log.debug("[SMB] Created %s (%d) for storing '%s'", filename, fid, requested_filename) - contents = "" + contents = b'' if requested_filename == VERIFIED_REQ: log.debug("[SMB] Verifying server is alive") diff --git a/tests/sshhelp.pm b/tests/sshhelp.pm index 41a21662618f..2481de3278de 100644 --- a/tests/sshhelp.pm +++ b/tests/sshhelp.pm @@ -342,7 +342,7 @@ sub find_sshkeygen { sub find_httptlssrv { my $p = find_exe_file_hpath($httptlssrvexe); if($p) { - my @o = `"$p" -l`; + my @o = qx("$p" -l); my $found; for(@o) { if(/Key exchange: SRP/) { diff --git a/tests/sshserver.pl b/tests/sshserver.pl index c4d0285782ab..2461b668b114 100755 --- a/tests/sshserver.pl +++ b/tests/sshserver.pl @@ -677,7 +677,7 @@ sub sshd_supports_opt { ($sshdid =~ /SunSSH/)) { # ssh daemon supports command line options -t -f and -o $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/, - `\"$sshd\" -t -f $sshdconfig_abs -o \"$option=$value\" 2>&1`; + qx(\"$sshd\" -t -f $sshdconfig_abs -o \"$option=$value\" 2>&1); return !$err; } if(($sshdid =~ /OpenSSH/) && ($sshdvernum >= 299)) { @@ -688,7 +688,7 @@ sub sshd_supports_opt { return 0; } $err = grep /((Unsupported)|(Bad configuration)|(Deprecated)) option.*$option/, - `\"$sshd\" -t -f $sshdconfig_abs 2>&1`; + qx(\"$sshd\" -t -f $sshdconfig_abs 2>&1); unlink $sshdconfig; return !$err; } @@ -1185,12 +1185,12 @@ sub sshd_supports_opt { # Put an "exec" in front of the command so that the child process # keeps this child's process ID by being tied to the spawned shell. - exec("exec $cmd") || die "Cannot exec() $cmd: $!"; - # exec() will create a new process, but ties the existence of the + exec("exec $cmd") or die "Cannot exec() $cmd: $!"; + # exec() creates a new process, but ties the existence of the # new process to the parent waiting perl.exe and sh.exe processes. # exec() should never return back here to this process. We protect - # ourselves by calling die() just in case something goes really bad. + # ourselves by calling die() in case something goes really bad. die "error: exec() has returned"; } diff --git a/tests/test1119.pl b/tests/test1119.pl index 9004696f6f55..47ec214120c1 100755 --- a/tests/test1119.pl +++ b/tests/test1119.pl @@ -48,25 +48,25 @@ } # we may get the directory root pointed out -my $root=$ARGV[0] || "."; +my $root = $ARGV[0] || "."; # need an include directory when building out-of-tree my $i = ($ARGV[1]) ? "-I$ARGV[1] " : ''; -my $verbose=0; -my $summary=0; -my $misses=0; +my $verbose = 0; +my $summary = 0; +my $misses = 0; my @manrefs; my @syms; my %doc; my %rem; -# scanenum runs the preprocessor on curl.h so it will process all enums +# scanenum runs the preprocessor on curl.h so it processes all enums # included by it, which *should* be all headers sub scanenum { my ($file) = @_; - open my $h_in, "-|", "$Cpreprocessor $i$file" || die "Cannot preprocess $file"; + open my $h_in, "-|", "$Cpreprocessor $i$file" or die "Cannot preprocess $file"; while(<$h_in>) { if(/enum\s+(\S+\s+)?{/ .. /}/) { s/^\s+//; @@ -76,11 +76,11 @@ sub scanenum { push @syms, $_; } } - close $h_in || die "Error preprocessing $file"; + close $h_in or die "Error preprocessing $file"; } sub scanheader { - my ($f)=@_; + my ($f) = @_; open(my $h, "<", $f); while(<$h>) { if(/^#define ((LIB|)CURL[A-Za-z0-9_]*)/) { @@ -92,8 +92,7 @@ sub scanheader { sub scanallheaders { my $d = "$root/include/curl"; - opendir(my $dh, $d) || - die "Cannot opendir: $!"; + opendir(my $dh, $d) or die "Cannot opendir: $!"; my @headers = grep { /.h\z/ } readdir($dh); closedir $dh; foreach my $h (@headers) { @@ -130,8 +129,7 @@ sub checkmanpage { sub scanman_md_dir { my ($d) = @_; - opendir(my $dh, $d) || - die "Cannot opendir: $!"; + opendir(my $dh, $d) or die "Cannot opendir: $!"; my @mans = grep { /.md\z/ } readdir($dh); closedir $dh; for my $m (@mans) { @@ -146,26 +144,26 @@ sub scanman_md_dir { open(my $s, "<", "$root/docs/libcurl/symbols-in-versions"); while(<$s>) { if(/(^[^ \n]+) +(.*)/) { - my ($sym, $rest)=($1, $2); + my ($sym, $rest) = ($1, $2); if($doc{$sym}) { print "Detected duplicate symbol: $sym\n"; $misses++; next; } - $doc{$sym}=$sym; - my @a=split(/ +/, $rest); + $doc{$sym} = $sym; + my @a = split(/ +/, $rest); if($a[2]) { # this symbol is documented to have been present the last time # in this release - $rem{$sym}=$a[2]; + $rem{$sym} = $a[2]; } } } close $s; -my $ignored=0; +my $ignored = 0; for my $e (sort @syms) { - # OBSOLETE - names that are just placeholders for a position where we + # OBSOLETE - names that are placeholders for a position where we # previously had a name, that is now removed. The OBSOLETE names should # never be used for anything. # @@ -176,7 +174,7 @@ sub scanman_md_dir { # # CURL_TEMP_ - are defined and *undefined* again within the file # - # *_LAST and *_LASTENTRY are just suffix for the placeholders used for the + # *_LAST and *_LASTENTRY are suffix for the placeholders used for the # last entry in many enum series. # @@ -188,7 +186,7 @@ sub scanman_md_dir { if($verbose) { print $e."\n"; } - $doc{$e}="used"; + $doc{$e} = "used"; next; } else { @@ -223,7 +221,7 @@ sub scanman_md_dir { my %warned; for my $r (@manrefs) { if($r =~ /^([^:]+):(.*)/) { - my ($sym, $file)=($1, $2); + my ($sym, $file) = ($1, $2); if(!$doc{$sym} && !$warned{$sym, $file}) { print "$file: $sym is not a public symbol\n"; $warned{$sym, $file} = 1; diff --git a/tests/test1135.pl b/tests/test1135.pl index 40f18aaa2293..907a08d20bb2 100755 --- a/tests/test1135.pl +++ b/tests/test1135.pl @@ -51,7 +51,7 @@ } $root = "$root/include/curl"; -opendir(D, $root) || die "Cannot open directory $root: $!\n"; +opendir(D, $root) or die "Cannot open directory $root: $!\n"; my @dir = readdir(D); closedir(D); @@ -62,13 +62,13 @@ } } -my $verbose=0; -my $summary=0; -my $misses=0; +my $verbose = 0; +my $summary = 0; +my $misses = 0; my @out; foreach my $f (@incs) { - open H, "<$f" || die; + open H, "<$f" or die; my $first = ""; while() { s/CURL_DEPRECATED\(.*"\)//; diff --git a/tests/test1139.pl b/tests/test1139.pl index a11c9d03a3ba..81a33bb5375e 100755 --- a/tests/test1139.pl +++ b/tests/test1139.pl @@ -42,11 +42,11 @@ use warnings; # we may get the directory roots pointed out -my $root=$ARGV[0] || "."; -my $buildroot=$ARGV[1] || "."; +my $root = $ARGV[0] || "."; +my $buildroot = $ARGV[1] || "."; my $syms = "$root/docs/libcurl/symbols-in-versions"; my $curlh = "$root/include/curl/curl.h"; -my $errors=0; +my $errors = 0; # the prepopulated alias list is the CURLINFO_* defines that are used for the # debug function callback and the fact that they use the same prefix as the @@ -67,8 +67,7 @@ sub scanmdpage { my ($file, @words) = @_; - open(my $mh, "<", $file) || - die "could not open $file"; + open(my $mh, "<", $file) or die "could not open $file"; my @m; while(<$mh>) { if($_ =~ /^## (.*)/) { @@ -101,11 +100,10 @@ sub scanmdpage { my $r; # check for define aliases -open($r, "<", $curlh) || - die "no curl.h"; +open($r, "<", $curlh) or die "no curl.h"; while(<$r>) { if(/^\#define (CURL(OPT|INFO|MOPT)_\w+) (.*)/) { - $alias{$1}=$3; + $alias{$1} = $3; } } close($r); @@ -113,8 +111,7 @@ sub scanmdpage { my @curlopt; my @curlinfo; my @curlmopt; -open($r, "<", $syms) || - die "no input file"; +open($r, "<", $syms) or die "no input file"; while(<$r>) { chomp; my $l= $_; @@ -187,8 +184,7 @@ sub scanmdpage { ######################################################################### # parse the curl code that parses the command line arguments! -open($r, "<", "$root/src/tool_getparam.c") || - die "no input file"; +open($r, "<", "$root/src/tool_getparam.c") or die "no input file"; my $list; my @getparam; # store all parsed parameters @@ -198,11 +194,11 @@ sub scanmdpage { $no++; chomp; if(/struct LongShort aliases/) { - $list=1; + $list = 1; } elsif($list) { if(/^ \{(\"[^,]*\").*\'(.)\',/) { - my ($l, $s)=($1, $2); + my ($l, $s) = ($1, $2); my $sh; my $lo; my $title; @@ -212,12 +208,12 @@ sub scanmdpage { if($l =~ /\"(.*)\"/) { # long option $lo = $1; - $title="--$lo"; + $title = "--$lo"; } if($s ne " ") { # a short option $sh = $s; - $title="-$sh, $title"; + $title = "-$sh, $title"; } push @getparam, $title; $opts{$title} |= 1; @@ -230,7 +226,8 @@ sub scanmdpage { ######################################################################### # parse the curl.1 man page, extract all documented command line options # The man page may or may not be rebuilt, so check both possible locations -open($r, "<", "$buildroot/docs/cmdline-opts/curl.1") || open($r, "<", "$root/docs/cmdline-opts/curl.1") || +open($r, "<", "$buildroot/docs/cmdline-opts/curl.1") or + open($r, "<", "$root/docs/cmdline-opts/curl.1") or die "failed getting curl.1"; my @manpage; # store all parsed parameters while(<$r>) { @@ -258,14 +255,13 @@ sub scanmdpage { ######################################################################### # parse the curl code that outputs the curl -h list -open($r, "<", "$root/src/tool_listhelp.c") || - die "no input file"; +open($r, "<", "$root/src/tool_listhelp.c") or die "no input file"; my @toolhelp; # store all parsed parameters while(<$r>) { chomp; my $l= $_; if(/^ \{ \" *(.*)/) { - my $str=$1; + my $str = $1; my $combo; if($str =~ /^-(.), --([a-z0-9.-]*)/) { # figure out the -short, --long combo @@ -295,10 +291,10 @@ sub scanmdpage { my $exists; my $missing; if($where & 1) { - $exists=" tool_getparam.c"; + $exists = " tool_getparam.c"; } else { - $missing=" tool_getparam.c"; + $missing = " tool_getparam.c"; } if($where & 2) { $exists.= " curl.1"; diff --git a/tests/test1140.pl b/tests/test1140.pl index 45da989ea667..d7f56043f451 100755 --- a/tests/test1140.pl +++ b/tests/test1140.pl @@ -50,7 +50,7 @@ sub manpresent { elsif(-r "$docsroot/$man" || -r "$docsroot/libcurl/$man" || -r "$docsroot/libcurl/opts/$man") { - $manp{$man}=1; + $manp{$man} = 1; return 1; } return 0; @@ -58,14 +58,13 @@ sub manpresent { sub file { my ($f) = @_; - open(my $fh, "<", $f) || - die "test1140.pl could not open $f"; + open(my $fh, "<", $f) or die "test1140.pl could not open $f"; my $line = 1; while(<$fh>) { chomp; my $l = $_; while($l =~ s/\\f(.)([^ ]*)\\f(.)//) { - my ($pre, $str, $post)=($1, $2, $3); + my ($pre, $str, $post) = ($1, $2, $3); if($str =~ /^\\f[ib]/i) { print "error: $f:$line: double-highlight\n"; $errors++; diff --git a/tests/test1165.pl b/tests/test1165.pl index bcf8b50fb713..ef6b34f32856 100755 --- a/tests/test1165.pl +++ b/tests/test1165.pl @@ -38,15 +38,15 @@ my %docs; # we may get the directory root pointed out -my $root=$ARGV[0] || "."; -my $DOCS="CURL-DISABLE.md"; +my $root = $ARGV[0] || "."; +my $DOCS = "CURL-DISABLE.md"; sub scanconf { - my ($f)=@_; + my ($f) = @_; open S, "<$f"; while() { if(/(CURL_DISABLE_[A-Z0-9_]+)/g) { - my ($sym)=($1); + my ($sym) = ($1); if(not $sym =~ /^(CURL_DISABLE_TYPECHECK)$/) { $disable{$sym} = 1; } @@ -56,7 +56,7 @@ sub scanconf { } sub scan_configure { - opendir(my $m, "$root/m4") || die "Cannot opendir $root/m4: $!"; + opendir(my $m, "$root/m4") or die "Cannot opendir $root/m4: $!"; my @m4 = grep { /\.m4$/ } readdir($m); closedir $m; scanconf("$root/configure.ac"); @@ -67,11 +67,11 @@ sub scan_configure { } sub scanconf_cmake { - my ($hashr, $f)=@_; + my ($hashr, $f) = @_; open S, "<$f"; while() { if(/(CURL_DISABLE_[A-Z0-9_]+)/g) { - my ($sym)=($1); + my ($sym) = ($1); if(not $sym =~ /^(CURL_DISABLE_INSTALL|CURL_DISABLE_SRP|CURL_DISABLE_TYPECHECK)$/) { $hashr->{$sym} = 1; } @@ -94,11 +94,11 @@ sub scan_cmake_config_h { ); sub scan_file { - my ($source)=@_; + my ($source) = @_; open F, "<$source"; while() { while(s/(CURL_DISABLE_[A-Z0-9_]+)//) { - my ($sym)=($1); + my ($sym) = ($1); if(!$whitelisted{$sym}) { $file{$sym} = $source; @@ -109,8 +109,8 @@ sub scan_file { } sub scan_dir { - my ($dir)=@_; - opendir(my $dh, $dir) || die "Cannot opendir $dir: $!"; + my ($dir) = @_; + opendir(my $dh, $dir) or die "Cannot opendir $dir: $!"; my @cfiles = grep { /\.[ch]\z/ && -f "$dir/$_" } readdir($dh); closedir $dh; for my $f (sort @cfiles) { @@ -132,7 +132,7 @@ sub scan_docs { while() { $line++; if(/^## `(CURL_DISABLE_[A-Z0-9_]+)`/g) { - my ($sym)=($1); + my ($sym) = ($1); if(not $sym =~ /^(CURL_DISABLE_TYPECHECK)$/) { $docs{$sym} = $line; } diff --git a/tests/test1167.pl b/tests/test1167.pl index dd0097e3716a..a0bdb385db91 100755 --- a/tests/test1167.pl +++ b/tests/test1167.pl @@ -47,32 +47,32 @@ $Cpreprocessor = 'cpp'; } -my $verbose=0; +my $verbose = 0; # verbose mode when -v is the first argument if($ARGV[0] eq "-v") { - $verbose=1; + $verbose = 1; shift; } # we may get the directory root pointed out -my $root=$ARGV[0] || "."; +my $root = $ARGV[0] || "."; # need an include directory when building out-of-tree my $i = ($ARGV[1]) ? "-I$ARGV[1] " : ''; my $incdir = "$root/include/curl"; -my $summary=0; -my $misses=0; +my $summary = 0; +my $misses = 0; my @syms; sub scanenums { - my ($file)=@_; + my ($file) = @_; my $skipit = 0; - open H_IN, "-|", "$Cpreprocessor -DCURL_DISABLE_DEPRECATION $i$file" || + open(H_IN, "-|", "$Cpreprocessor -DCURL_DISABLE_DEPRECATION $i$file") or die "Cannot preprocess $file"; while() { my ($line, $linenum) = ($_, $.); @@ -113,11 +113,11 @@ sub scanenums { } } } - close H_IN || die "Error preprocessing $file"; + close H_IN or die "Error preprocessing $file"; } sub scanheader { - my ($f)=@_; + my ($f) = @_; scanenums($f); open(H, '<', $f); while() { @@ -134,7 +134,7 @@ sub scanheader { close H; } -opendir(my $dh, $incdir) || die "Cannot opendir $incdir: $!"; +opendir(my $dh, $incdir) or die "Cannot opendir $incdir: $!"; my @hfiles = grep { /\.h$/ } readdir($dh); closedir $dh; diff --git a/tests/test1173.pl b/tests/test1173.pl index 6e2c9141afca..14894c625bd0 100755 --- a/tests/test1173.pl +++ b/tests/test1173.pl @@ -32,10 +32,10 @@ use File::Basename; # get the filename first -my $symbolsinversions=shift @ARGV; +my $symbolsinversions = shift @ARGV; # we may get the directory roots pointed out -my @manpages=@ARGV; +my @manpages = @ARGV; my $errors = 0; my %docsdirs; @@ -79,15 +79,14 @@ CURLOPT_RANDOM_FILE => 1, ); sub allsymbols { - open(my $f, "<", $symbolsinversions) || - die "$symbolsinversions: $|"; + open(my $f, "<", $symbolsinversions) or die "$symbolsinversions: $|"; while(<$f>) { if($_ =~ /^([^ ]*) +(.*)/) { my ($name, $info) = ($1, $2); - $symbol{$name}=$name; + $symbol{$name} = $name; if($info =~ /([0-9.]+) +([0-9.]+)/) { - $deprecated{$name}=$info; + $deprecated{$name} = $info; } } } @@ -98,7 +97,7 @@ sub allsymbols { 'curl.1' => 1 ); sub checkref { - my ($f, $sec, $file, $line)=@_; + my ($f, $sec, $file, $line) = @_; my $present = 0; #print STDERR "check $f.$sec\n"; if($ref{"$f.$sec"}) { @@ -108,7 +107,7 @@ sub checkref { foreach my $d (keys %docsdirs) { if(-f "$d/$f.$sec") { $present = 1; - $ref{"$f.$sec"}=1; + $ref{"$f.$sec"} = 1; last; } } @@ -140,12 +139,11 @@ sub scanmanpage { my $shc = 0; my $optpage = 0; # option or function my @sh; - my $SH=""; + my $SH = ""; my @separators; my @sepline; - open(my $m, "<", $file) || - die "test1173.pl could not open $file"; + open(my $m, "<", $file) or die "test1173.pl could not open $file"; if($file =~ /[\/\\](CURL|curl_)([^\/\\]*).3/) { # This is a man page for libcurl. It requires an example unless it is # considered deprecated. @@ -158,7 +156,7 @@ sub scanmanpage { while(<$m>) { chomp; if($_ =~ /^.so /) { - # this man page is just a referral + # this man page is a referral close($m); return; } diff --git a/tests/test1175.pl b/tests/test1175.pl index 54e0a1fe762e..b929ff41ee86 100755 --- a/tests/test1175.pl +++ b/tests/test1175.pl @@ -41,7 +41,7 @@ sub getdocserrors { ; } else { - $docs{$symbol}=1; + $docs{$symbol} = 1; } } } @@ -57,7 +57,7 @@ sub getincludeerrors { # removed! } else { - $error{$symbol}=$added; + $error{$symbol} = $added; } } } diff --git a/tests/test1177.pl b/tests/test1177.pl index 32377986a585..cf5bd44fa744 100755 --- a/tests/test1177.pl +++ b/tests/test1177.pl @@ -30,14 +30,14 @@ use strict; use warnings; -my $manpage=$ARGV[0]; -my $header=$ARGV[1]; -my $source=$ARGV[2]; +my $manpage = $ARGV[0]; +my $header = $ARGV[1]; +my $source = $ARGV[2]; my %manversion; my %headerversion; my %manname; my %sourcename; -my $error=0; +my $error = 0; open(my $m, "<", $manpage); while(<$m>) { diff --git a/tests/test1222.pl b/tests/test1222.pl index 96f6d60b23e2..0d6a5ffc2cd6 100755 --- a/tests/test1222.pl +++ b/tests/test1222.pl @@ -54,7 +54,7 @@ # Scan header file for public function and enum values. Flag them with # the version they are deprecated in, if some. sub scan_header { - my ($f)=@_; + my ($f) = @_; my $line = ""; my $incomment = 0; my $inenum = 0; @@ -147,7 +147,7 @@ sub scan_header { # Each option has to be declared as ".IP