diff --git a/.circleci/config.yml b/.circleci/config.yml index e554648a97cfed..02c8ec006de1c7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,7 @@ commands: - run: command: | # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSL_VER=5.6.0 + WOLFSSL_VER=5.7.4 echo "Installing wolfSSL $WOLFSSL_VER" curl -LOsSf --retry 6 --retry-connrefused --max-time 999 https://github.com/wolfSSL/wolfssl/archive/v$WOLFSSL_VER-stable.tar.gz tar -xzf v$WOLFSSL_VER-stable.tar.gz @@ -66,7 +66,7 @@ commands: - run: command: | # renovate: datasource=github-tags depName=wolfSSL/wolfssh versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - WOLFSSH_VER=1.4.12 + WOLFSSH_VER=1.4.19 echo "Installing wolfSSH $WOLFSSH_VER" curl -LOsSf --retry 6 --retry-connrefused --max-time 999 https://github.com/wolfSSL/wolfssh/archive/v$WOLFSSH_VER-stable.tar.gz tar -xzf v$WOLFSSH_VER-stable.tar.gz diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 18be9ed224a463..0cb7f45a11f2c4 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -26,4 +26,4 @@ Send your suggestions using one of these methods: 3. as an [issue](https://github.com/curl/curl/issues) -/ The curl team! +/ The curl team diff --git a/.github/scripts/badwords.txt b/.github/scripts/badwords.txt index 5bab85fbe48905..0af80b1dc744f0 100644 --- a/.github/scripts/badwords.txt +++ b/.github/scripts/badwords.txt @@ -65,3 +65,4 @@ couldn't:could not 64 bit\b=64-bit 64-bits:64 bits or 64-bit 32-bits:32 bits or 32-bit +\bvery\b:rephrase using an alternative word diff --git a/.github/scripts/cmp-config.pl b/.github/scripts/cmp-config.pl index f573891cc4eae8..c58e3a6dfd1b26 100755 --- a/.github/scripts/cmp-config.pl +++ b/.github/scripts/cmp-config.pl @@ -48,6 +48,7 @@ '#define HAVE_DECL_GETPWUID_R_MISSING 1' => 1, '#define HAVE_DLFCN_H 1' => 1, '#define HAVE_GETHOSTBYNAME 1' => 1, + '#define HAVE_GSSAPI_GSSAPI_KRB5_H 1' => 1, '#define HAVE_INTTYPES_H 1' => 1, '#define HAVE_IOCTL 1' => 1, '#define HAVE_LDAP_H 1' => 1, @@ -75,8 +76,8 @@ '#define HAVE_ZSTD_H 1' => 1, '#define LT_OBJDIR ".libs/"' => 1, '#define NEED_LBER_H 1' => 1, - '#define OS "Linux"' => 1, - '#define OS "x86_64-pc-linux-gnu"' => 1, + '#define CURL_OS "Linux"' => 1, + '#define CURL_OS "x86_64-pc-linux-gnu"' => 1, '#define PACKAGE "curl"' => 1, '#define PACKAGE_BUGREPORT "a suitable curl mailing list: https://curl.se/mail/"' => 1, '#define PACKAGE_NAME "curl"' => 1, diff --git a/.github/scripts/spellcheck.words b/.github/scripts/spellcheck.words index 4254ec9557f89d..7aa13af7075b92 100644 --- a/.github/scripts/spellcheck.words +++ b/.github/scripts/spellcheck.words @@ -364,7 +364,7 @@ ifdefs ifhost IIS ILE -Illumos +illumos IMAP imap IMAPS @@ -688,7 +688,6 @@ RETR retransmit retrigger RHEL -RICS Rikard rmdir ROADMAP @@ -702,6 +701,7 @@ RRtype RSA RTMP rtmp +rtmpdump RTMPE RTMPS RTMPT @@ -768,6 +768,7 @@ SOCKOPT SOCKSv Solaris SONAME +SOVERSION Soref SPARC SPDX diff --git a/.github/workflows/checkdocs.yml b/.github/workflows/checkdocs.yml index 750b9794555ce1..81cc5ca6979f1e 100644 --- a/.github/workflows/checkdocs.yml +++ b/.github/workflows/checkdocs.yml @@ -33,59 +33,63 @@ concurrency: permissions: {} jobs: - proselint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 - name: checkout - - - name: install prereqs - run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - sudo apt-get install python3-proselint - - # config file help: https://github.com/amperser/proselint/ - - name: create proselint config - run: | - cat < $HOME/.proselintrc - { - "checks": { - "typography.diacritical_marks": false, - "typography.symbols": false, - "annotations.misc": false, - "security.password": false - } - } - JSON - - - name: trim headers off all *.md files - run: git ls-files -z '*.md' | xargs -0 -n1 .github/scripts/trimmarkdownheader.pl - - - name: check prose - run: git ls-files -z '*.md' | grep -Evz 'CHECKSRC.md|DISTROS.md|CURLOPT_INTERFACE.md|interface.md' | xargs -0 proselint README - - # This is for CHECKSRC and files with aggressive exclamation mark needs - - name: create second proselint config - run: | - cat < $HOME/.proselintrc - { - "checks": { - "typography.diacritical_marks": false, - "typography.symbols": false, - "typography.exclamation": false, - "annotations.misc": false - } - } - JSON - - - name: check special prose - run: proselint docs/CHECKSRC.md docs/libcurl/opts/CURLOPT_INTERFACE.md docs/cmdline-opts/interface.md + # proselint: + # runs-on: ubuntu-latest + # steps: + # - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4 + # name: checkout + # + # - name: install prereqs + # run: | + # sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + # sudo apt-get update -y + # sudo apt-get install -y --no-install-suggests --no-install-recommends \ + # python3-proselint + # + # # config file help: https://github.com/amperser/proselint/ + # - name: create proselint config + # run: | + # cat < $HOME/.proselintrc.json + # { + # "checks": { + # "typography.diacritical_marks": false, + # "typography.symbols": false, + # "annotations.misc": false, + # "security.password": false, + # "misc.annotations": false + # } + # } + # JSON + # + # - name: trim headers off all *.md files + # run: git ls-files -z '*.md' | xargs -0 -n1 .github/scripts/trimmarkdownheader.pl + # + # - name: check prose + # run: git ls-files -z '*.md' | grep -Evz 'CHECKSRC.md|DISTROS.md|curl_mprintf.md|CURLOPT_INTERFACE.md|interface.md' | xargs -0 proselint README + # + # # This is for CHECKSRC and files with aggressive exclamation mark needs + # - name: create second proselint config + # run: | + # cat < $HOME/.proselintrc.json + # { + # "checks": { + # "typography.diacritical_marks": false, + # "typography.symbols": false, + # "typography.exclamation": false, + # "lexical_illusions.misc": false, + # "annotations.misc": false + # } + # } + # JSON + # + # - name: check special prose + # run: proselint docs/internals/CHECKSRC.md docs/libcurl/curl_mprintf.md docs/libcurl/opts/CURLOPT_INTERFACE.md docs/cmdline-opts/interface.md # Docs: https://github.com/marketplace/actions/markdown-link-check linkcheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 name: checkout - name: trim the cmdline docs markdown files @@ -98,7 +102,7 @@ jobs: spellcheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 name: checkout - name: trim all man page *.md files @@ -117,14 +121,14 @@ jobs: run: grep -v '^#' .github/scripts/spellcheck.words > wordlist.txt - name: Check Spelling - uses: rojopolis/spellcheck-github-actions@b83ca7c1b5c285e4f2b43e209a455c74872ec341 # v0 + uses: rojopolis/spellcheck-github-actions@403efe0642148e94ecb3515e89c767b85a32371a # v0 with: config_path: .github/scripts/spellcheck.yaml badwords-synopsis: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 name: checkout - name: badwords @@ -136,7 +140,7 @@ jobs: man-examples: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 name: checkout - name: render nroff versions @@ -144,3 +148,13 @@ jobs: - name: verify examples run: .github/scripts/verify-examples.pl docs/libcurl/curl*.3 docs/libcurl/opts/*.3 + + miscchecks: + runs-on: ubuntu-24.04 + timeout-minutes: 5 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + name: checkout + + - name: spacecheck + run: .github/scripts/spacecheck.pl diff --git a/.github/workflows/checksrc.yml b/.github/workflows/checksrc.yml index 2ebc01304232a1..02c864b9bdc176 100644 --- a/.github/workflows/checksrc.yml +++ b/.github/workflows/checksrc.yml @@ -35,22 +35,30 @@ jobs: checksrc: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 name: checkout - name: check run: git ls-files -z "*.[ch]" | xargs -0 -n1 ./scripts/checksrc.pl - codespell-cmakelint: - runs-on: ubuntu-latest + codespell-cmakelint-pytype-ruff: + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 name: checkout - name: install + env: + DEBIAN_FRONTEND: noninteractive run: | - sudo apt-get install codespell python3-pip - python3 -m pip install cmakelint==1.4.3 + sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo apt-get update -y + sudo apt-get install -y --no-install-suggests --no-install-recommends \ + codespell python3-pip python3-networkx python3-pydot python3-yaml \ + python3-toml python3-markupsafe python3-jinja2 python3-tabulate \ + python3-typing-extensions python3-libcst python3-impacket \ + python3-websockets python3-pytest + python3 -m pip install --break-system-packages cmakelint==1.4.3 pytype==2024.9.13 ruff==0.6.8 - name: spellcheck run: | @@ -63,20 +71,26 @@ jobs: - name: cmakelint run: scripts/cmakelint.sh + - name: pytype + run: find . -name '*.py' -exec pytype -j auto -k {} + + + - name: ruff + run: 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 + reuse: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 name: checkout - name: REUSE Compliance Check - uses: fsfe/reuse-action@3ae3c6bdf1257ab19397fab11fd3312144692083 # v4 + uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5 miscchecks: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 name: checkout - name: shellcheck diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 18cfb85d7a4f9f..6ea8e7049eebc8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,11 +47,11 @@ jobs: security-events: write steps: - name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3 + uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3 with: languages: cpp queries: security-extended @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3 + uses: github/codeql-action/autobuild@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -73,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@e2b3eafc8d227b0241d48be5f425d47c2d750a13 # v3 + uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3 diff --git a/.github/workflows/configure-vs-cmake.yml b/.github/workflows/configure-vs-cmake.yml index 11e96aafceab0d..57ec347b3f6231 100644 --- a/.github/workflows/configure-vs-cmake.yml +++ b/.github/workflows/configure-vs-cmake.yml @@ -32,7 +32,7 @@ jobs: check-linux: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: run configure --with-openssl run: | @@ -70,7 +70,7 @@ jobs: run: | echo '::group::brew packages installed'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::' - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: run configure --with-openssl run: | @@ -107,7 +107,7 @@ jobs: - name: install packages run: sudo apt-get --quiet 2 --option Dpkg::Use-Pty=0 install mingw-w64 - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: run configure --with-schannel run: | diff --git a/.github/workflows/curl-for-win.yml b/.github/workflows/curl-for-win.yml index 4a23c0523a4b1e..d844d414b4758d 100644 --- a/.github/workflows/curl-for-win.yml +++ b/.github/workflows/curl-for-win.yml @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: path: 'curl' fetch-depth: 8 @@ -73,7 +73,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: path: 'curl' fetch-depth: 8 @@ -99,7 +99,7 @@ jobs: env: CW_JOBS: '4' steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: path: 'curl' fetch-depth: 8 @@ -114,7 +114,7 @@ jobs: win-llvm: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: path: 'curl' fetch-depth: 8 diff --git a/.github/workflows/distcheck.yml b/.github/workflows/distcheck.yml index d97596db6acc6c..8790157f9aa0f0 100644 --- a/.github/workflows/distcheck.yml +++ b/.github/workflows/distcheck.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - run: sudo apt-get purge -y curl libcurl4 libcurl4-doc name: 'remove preinstalled curl libcurl4{-doc}' @@ -42,7 +42,7 @@ jobs: run: | SOURCE_DATE_EPOCH=1711526400 ./scripts/maketgz 99.98.97 - - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4 + - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4 with: name: 'release-tgz' path: 'curl-99.98.97.tar.gz' @@ -128,7 +128,7 @@ jobs: timeout-minutes: 5 needs: maketgz-and-verify-in-tree steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4 with: @@ -140,7 +140,7 @@ jobs: reproducible-releases: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - run: sudo apt-get purge -y curl libcurl4 libcurl4-doc name: 'remove preinstalled curl libcurl4{-doc}' diff --git a/.github/workflows/hacktoberfest-accepted.yml b/.github/workflows/hacktoberfest-accepted.yml index 8574d4a4686e08..2b8d0acb89d938 100644 --- a/.github/workflows/hacktoberfest-accepted.yml +++ b/.github/workflows/hacktoberfest-accepted.yml @@ -26,7 +26,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: fetch-depth: 100 diff --git a/.github/workflows/http3-linux.yml b/.github/workflows/http3-linux.yml index bd28bd9851ac2a..e4301f03e79faa 100644 --- a/.github/workflows/http3-linux.yml +++ b/.github/workflows/http3-linux.yml @@ -43,22 +43,20 @@ permissions: {} env: MAKEFLAGS: -j 5 # handled in renovate.json - openssl3-version: openssl-3.3.0 - # unhandled - quictls-version: 3.1.4+quic + openssl-version: 3.4.0 + # handled in renovate.json + quictls-version: 3.3.0 # renovate: datasource=github-tags depName=gnutls/gnutls versioning=semver registryUrl=https://github.com - gnutls-version: 3.8.7 + gnutls-version: 3.8.8 wolfssl-version: master # renovate: datasource=github-tags depName=ngtcp2/nghttp3 versioning=semver registryUrl=https://github.com - nghttp3-version: 1.5.0 + nghttp3-version: 1.6.0 # renovate: datasource=github-tags depName=ngtcp2/ngtcp2 versioning=semver registryUrl=https://github.com - ngtcp2-version: 1.7.0 + ngtcp2-version: 1.8.1 # renovate: datasource=github-tags depName=nghttp2/nghttp2 versioning=semver registryUrl=https://github.com - nghttp2-version: 1.62.1 + nghttp2-version: 1.64.0 # renovate: datasource=github-tags depName=cloudflare/quiche versioning=semver registryUrl=https://github.com quiche-version: 0.22.0 - # renovate: datasource=github-tags depName=icing/mod_h2 versioning=semver registryUrl=https://github.com - mod_h2-version: 2.0.29 jobs: setup: @@ -92,16 +90,16 @@ jobs: steps: - name: cache quictls - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-quictls-no-deprecated env: cache-name: cache-quictls-no-deprecated with: path: /home/runner/quictls/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.quictls-version }} + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.quictls-version }}-quic1 - name: cache gnutls - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-gnutls env: cache-name: cache-gnutls @@ -110,7 +108,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.gnutls-version }} - name: cache wolfssl - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -120,7 +118,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.wolfssl-version }} - name: cache nghttp3 - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -129,7 +127,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.nghttp3-version }} - name: cache ngtcp2 - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 @@ -138,7 +136,7 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.ngtcp2-version }} - name: cache nghttp2 - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-nghttp2 env: cache-name: cache-nghttp2 @@ -157,12 +155,13 @@ jobs: run: | echo 'needs-build=true' >> $GITHUB_OUTPUT - - name: install build prerequisites + - name: install build prereqs if: steps.settings.outputs.needs-build == 'true' run: | sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list sudo apt-get update -y - sudo apt-get install libtool autoconf automake pkgconf stunnel4 \ + sudo apt-get install -y --no-install-suggests --no-install-recommends \ + libtool autoconf automake pkgconf stunnel4 \ libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libev-dev libc-ares-dev \ nettle-dev libp11-kit-dev libtspi-dev libunistring-dev guile-2.2-dev libtasn1-bin \ libtasn1-6-dev libidn2-0-dev gawk gperf libtss2-dev dns-root-data bison gtk-doc-tools \ @@ -174,7 +173,7 @@ jobs: - if: steps.cache-quictls-no-deprecated.outputs.cache-hit != 'true' run: | cd $HOME - git clone --quiet --depth=1 -b openssl-${{ env.quictls-version }} https://github.com/quictls/openssl quictls + git clone --quiet --depth=1 -b openssl-${{ env.quictls-version }}-quic1 https://github.com/quictls/openssl quictls cd quictls ./config no-deprecated --prefix=$PWD/build --libdir=lib make @@ -217,7 +216,7 @@ jobs: cd $HOME git clone --quiet --depth=1 -b v${{ env.nghttp3-version }} https://github.com/ngtcp2/nghttp3 cd nghttp3 - git submodule update --init + git submodule update --init --depth=1 autoreconf -fi ./configure --disable-dependency-tracking --prefix=$PWD/build PKG_CONFIG_PATH="$PWD/build/lib/pkgconfig" --enable-lib-only make @@ -249,71 +248,95 @@ jobs: make install name: 'build nghttp2' - autotools: - name: ${{ matrix.build.name }} + linux: + name: ${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.build.name }} needs: - setup - build-cache - runs-on: 'ubuntu-latest' - timeout-minutes: 60 + runs-on: 'ubuntu-24.04' + timeout-minutes: 45 strategy: fail-fast: false matrix: build: - name: quictls + PKG_CONFIG_PATH: '$HOME/quictls/build/lib/pkgconfig:$HOME/nghttp3/build/lib/pkgconfig:$HOME/ngtcp2/build/lib/pkgconfig:$HOME/nghttp2/build/lib/pkgconfig' configure: >- - PKG_CONFIG_PATH="$HOME/quictls/build/lib/pkgconfig:$HOME/nghttp3/build/lib/pkgconfig:$HOME/ngtcp2/build/lib/pkgconfig:$HOME/nghttp2/build/lib/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/quictls/build/lib" + LDFLAGS="-Wl,-rpath,$HOME/quictls/build/lib" --with-ngtcp2=$HOME/ngtcp2/build --enable-warnings --enable-werror --enable-debug --disable-ntlm --with-test-nghttpx="$HOME/nghttp2/build/bin/nghttpx" --with-openssl=$HOME/quictls/build - name: gnutls + PKG_CONFIG_PATH: '$HOME/gnutls/build/lib/pkgconfig:$HOME/nghttp3/build/lib/pkgconfig:$HOME/ngtcp2/build/lib/pkgconfig:$HOME/nghttp2/build/lib/pkgconfig' configure: >- - PKG_CONFIG_PATH="$HOME/gnutls/build/lib/pkgconfig:$HOME/nghttp3/build/lib/pkgconfig:$HOME/ngtcp2/build/lib/pkgconfig:$HOME/nghttp2/build/lib/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/gnutls/build/lib" + LDFLAGS="-Wl,-rpath,$HOME/gnutls/build/lib" --with-ngtcp2=$HOME/ngtcp2/build --enable-warnings --enable-werror --enable-debug --with-test-nghttpx="$HOME/nghttp2/build/bin/nghttpx" --with-gnutls=$HOME/gnutls/build - name: wolfssl + PKG_CONFIG_PATH: '$HOME/wolfssl/build/lib/pkgconfig:$HOME/nghttp3/build/lib/pkgconfig:$HOME/ngtcp2/build/lib/pkgconfig:$HOME/nghttp2/build/lib/pkgconfig' configure: >- - PKG_CONFIG_PATH="$HOME/wolfssl/build/lib/pkgconfig:$HOME/nghttp3/build/lib/pkgconfig:$HOME/ngtcp2/build/lib/pkgconfig:$HOME/nghttp2/build/lib/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/wolfssl/build/lib" + LDFLAGS="-Wl,-rpath,$HOME/wolfssl/build/lib" --with-ngtcp2=$HOME/ngtcp2/build --enable-warnings --enable-werror --enable-debug --with-test-nghttpx="$HOME/nghttp2/build/bin/nghttpx" --with-wolfssl=$HOME/wolfssl/build + --enable-httpsrr --enable-ech + + - name: wolfssl + PKG_CONFIG_PATH: '$HOME/wolfssl/build/lib/pkgconfig:$HOME/nghttp3/build/lib/pkgconfig:$HOME/ngtcp2/build/lib/pkgconfig:$HOME/nghttp2/build/lib/pkgconfig' + generate: >- + -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON -DENABLE_DEBUG=ON + -DTEST_NGHTTPX="$HOME/nghttp2/build/bin/nghttpx" + -DHTTPD_NGHTTPX="$HOME/nghttp2/build/bin/nghttpx" + -DUSE_HTTPSRR=ON -DUSE_ECH=ON - name: openssl-quic + PKG_CONFIG_PATH: '$HOME/openssl/build/lib64/pkgconfig' configure: >- - PKG_CONFIG_PATH="$HOME/openssl3/build/lib64/pkgconfig" LDFLAGS="-Wl,-rpath,$HOME/openssl3/build/lib64" + LDFLAGS="-Wl,-rpath,$HOME/openssl/build/lib64" --enable-warnings --enable-werror --enable-debug --disable-ntlm --with-test-nghttpx="$HOME/nghttp2/build/bin/nghttpx" - --with-openssl=$HOME/openssl3/build --with-openssl-quic + --with-openssl=$HOME/openssl/build --with-openssl-quic --with-nghttp3=$HOME/nghttp3/build - name: quiche configure: >- - LDFLAGS="-Wl,-rpath,/home/runner/quiche/target/release" - --with-openssl=/home/runner/quiche/quiche/deps/boringssl/src + LDFLAGS="-Wl,-rpath,$HOME/quiche/target/release" + --with-openssl=$HOME/quiche/quiche/deps/boringssl/src --enable-warnings --enable-werror --enable-debug - --with-quiche=/home/runner/quiche/target/release + --with-quiche=$HOME/quiche/target/release --with-test-nghttpx="$HOME/nghttp2/build/bin/nghttpx" --with-ca-fallback + - name: quiche + PKG_CONFIG_PATH: '$HOME/quiche/target/release' + generate: >- + -DOPENSSL_ROOT_DIR=$HOME/quiche/quiche/deps/boringssl/src -DENABLE_DEBUG=ON + -DUSE_QUICHE=ON + -DTEST_NGHTTPX="$HOME/nghttp2/build/bin/nghttpx" + -DHTTPD_NGHTTPX="$HOME/nghttp2/build/bin/nghttpx" + -DCURL_CA_FALLBACK=ON + steps: - run: | sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list sudo apt-get update -y - sudo apt-get install libtool autoconf automake pkgconf stunnel4 \ + sudo apt-get install -y --no-install-suggests --no-install-recommends \ + libtool autoconf automake ninja-build pkgconf stunnel4 \ libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libev-dev libc-ares-dev \ nettle-dev libp11-kit-dev libtspi-dev libunistring-dev guile-2.2-dev libtasn1-bin \ libtasn1-6-dev libidn2-0-dev gawk gperf libtss2-dev dns-root-data bison gtk-doc-tools \ texinfo texlive texlive-extra-utils autopoint libev-dev \ apache2 apache2-dev libnghttp2-dev vsftpd + python3 -m venv $HOME/venv echo 'CC=gcc-12' >> $GITHUB_ENV echo 'CXX=g++-12' >> $GITHUB_ENV - name: 'install prereqs and impacket, pytest, crypto, apache2' + name: 'install prereqs' - name: cache quictls - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-quictls-no-deprecated env: cache-name: cache-quictls-no-deprecated @@ -323,7 +346,8 @@ jobs: fail-on-cache-miss: true - name: cache gnutls - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + if: matrix.build.name == 'gnutls' + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-gnutls env: cache-name: cache-gnutls @@ -333,7 +357,8 @@ jobs: fail-on-cache-miss: true - name: cache wolfssl - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + if: matrix.build.name == 'wolfssl' + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-wolfssl env: cache-name: cache-wolfssl @@ -344,7 +369,7 @@ jobs: fail-on-cache-miss: true - name: cache nghttp3 - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-nghttp3 env: cache-name: cache-nghttp3 @@ -354,7 +379,7 @@ jobs: fail-on-cache-miss: true - name: cache ngtcp2 - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-ngtcp2 env: cache-name: cache-ngtcp2 @@ -364,7 +389,7 @@ jobs: fail-on-cache-miss: true - name: cache nghttp2 - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-nghttp2 env: cache-name: cache-nghttp2 @@ -373,28 +398,28 @@ jobs: key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.nghttp2-version }} fail-on-cache-miss: true - - name: cache openssl3 + - name: cache openssl if: matrix.build.name == 'openssl-quic' - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 - id: cache-openssl3 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 + id: cache-openssl env: - cache-name: cache-openssl3 + cache-name: cache-openssl with: - path: /home/runner/openssl3/build - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.openssl3-version }} + path: /home/runner/openssl/build + key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.openssl-version }} - - name: 'install openssl3' - if: matrix.build.name == 'openssl-quic' && steps.cache-openssl3.outputs.cache-hit != 'true' + - name: 'install openssl' + if: matrix.build.name == 'openssl-quic' && steps.cache-openssl.outputs.cache-hit != 'true' run: | - git clone --quiet --depth=1 -b ${{ env.openssl3-version }} https://github.com/openssl/openssl + git clone --quiet --depth=1 -b openssl-${{ env.openssl-version }} https://github.com/openssl/openssl cd openssl - ./config --prefix=$HOME/openssl3/build + ./config --prefix=$HOME/openssl/build make -j1 install_sw cat exporters/openssl.pc - name: cache quiche if: matrix.build.name == 'quiche' - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-quiche env: cache-name: cache-quiche @@ -417,63 +442,103 @@ jobs: ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) quiche/deps/boringssl/src/lib/ # include dir - # /home/runner/quiche/quiche/deps/boringssl/src/include + # $HOME/quiche/quiche/deps/boringssl/src/include # lib dir - # /home/runner/quiche/quiche/deps/boringssl/src/lib + # $HOME/quiche/quiche/deps/boringssl/src/lib name: 'build quiche and boringssl' - - name: cache mod_h2 - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 - id: cache-mod_h2 - env: - cache-name: cache-mod_h2 - with: - path: /home/runner/mod_h2 - key: ${{ runner.os }}-http3-build-${{ env.cache-name }}-${{ env.mod_h2-version }} + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - if: steps.cache-mod_h2.outputs.cache-hit != 'true' - run: | - cd $HOME - git clone --quiet --depth=1 -b v${{ env.mod_h2-version }} https://github.com/icing/mod_h2 - cd mod_h2 - autoreconf -fi - ./configure - make - name: 'build mod_h2' - - - run: | - cd $HOME/mod_h2 - sudo make install - name: 'install mod_h2' + - run: autoreconf -fi + if: ${{ matrix.build.configure }} + name: 'autoreconf' - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - name: 'configure' + run: | + if [ -n '${{ matrix.build.PKG_CONFIG_PATH }}' ]; then + export PKG_CONFIG_PATH="${{ matrix.build.PKG_CONFIG_PATH }}" + fi + if [ -n '${{ matrix.build.generate }}' ]; then + cmake -B . -G Ninja \ + -DCMAKE_C_COMPILER_TARGET=$(uname -m)-pc-linux-gnu -DBUILD_STATIC_LIBS=ON \ + -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \ + -DCURL_BROTLI=ON -DCURL_ZSTD=ON \ + ${{ matrix.build.generate }} + else + ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-warnings --enable-werror \ + ${{ matrix.build.configure }} + fi + + - name: 'configure log' + if: ${{ !cancelled() }} + run: cat config.log CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true + + - name: 'curl_config.h' + run: | + echo '::group::raw'; cat lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' lib/curl_config.h | sort || true - - run: | - sudo python3 -m pip install -r tests/requirements.txt -r tests/http/requirements.txt - name: 'install python test prereqs' + - name: 'test configs' + run: | + cat tests/config || true + cat tests/http/config.ini || true - - run: autoreconf -fi - name: 'autoreconf' + - name: 'build' + run: | + if [ -n '${{ matrix.build.generate }}' ]; then + cmake --build . --verbose + else + make V=1 + fi - - run: ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles ${{ matrix.build.configure }} - name: 'configure' + - run: ./src/curl -V + name: 'check curl -V output' - - run: make V=1 - name: 'make' + - name: 'build tests' + run: | + if [ -n '${{ matrix.build.generate }}' ]; then + cmake --build . --verbose --target testdeps + else + make V=1 -C tests + fi - - run: make V=1 -C tests - name: 'make tests' + - name: 'install test prereqs' + run: | + source $HOME/venv/bin/activate + python3 -m pip install -r tests/requirements.txt - - run: make V=1 test-ci - name: 'run tests' + - name: 'run tests' env: TFLAGS: "${{ matrix.build.tflags }}" + run: | + source $HOME/venv/bin/activate + if [ -n '${{ matrix.build.generate }}' ]; then + cmake --build . --verbose --target test-ci + else + make V=1 test-ci + fi + + - name: 'install pytest prereqs' + run: | + source $HOME/venv/bin/activate + python3 -m pip install -r tests/http/requirements.txt - - run: make pytest-ci - name: 'run pytest' + - name: 'run pytest' env: TFLAGS: "${{ matrix.build.tflags }}" CURL_CI: github - - - run: make V=1 examples - name: 'make examples' + run: | + source $HOME/venv/bin/activate + if [ -n '${{ matrix.build.generate }}' ]; then + cmake --build . --verbose --target curl-pytest-ci + else + make V=1 pytest-ci + fi + + - name: 'build examples' + run: | + if [ -n '${{ matrix.build.generate }}' ]; then + cmake --build . --verbose --target curl-examples + else + make V=1 examples + fi diff --git a/.github/workflows/linux-old.yml b/.github/workflows/linux-old.yml index 4a7a361a019035..a4adebbcce2e17 100644 --- a/.github/workflows/linux-old.yml +++ b/.github/workflows/linux-old.yml @@ -73,7 +73,7 @@ jobs: httrack --get https://security.debian.org/debian-security/pool/updates/main/g/glibc/libc6_2.28-10+deb10u4_amd64.deb dpkg -i libc6_*_amd64.deb - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'cmake build-only (out-of-tree, libssh2)' run: | @@ -84,6 +84,11 @@ jobs: make install src/curl --disable --version + - name: 'cmake build-only curl_config.h' + run: | + echo '::group::raw'; cat bld-1/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld-1/lib/curl_config.h | sort || true + - name: 'cmake generate (out-of-tree, c-ares, libssh, zstd, gssapi)' run: | mkdir bld-cares @@ -92,6 +97,11 @@ jobs: -DENABLE_ARES=ON -DCURL_ZSTD=ON -DCURL_USE_GSSAPI=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON -DUSE_LIBRTMP=ON \ -DCURL_LIBCURL_VERSIONED_SYMBOLS=ON + - name: 'cmake curl_config.h' + run: | + echo '::group::raw'; cat bld-cares/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld-cares/lib/curl_config.h | sort || true + - name: 'cmake build' run: | make -C bld-cares @@ -109,7 +119,7 @@ jobs: - name: 'autoreconf' run: autoreconf -if - - name: 'configure (out-of-tree, libssh2)' + - name: 'configure (out-of-tree, c-ares, libssh, zstd, gssapi)' run: | mkdir bld-am cd bld-am @@ -117,6 +127,11 @@ jobs: --with-openssl --enable-ares --with-libssh --with-zstd --with-gssapi --with-librtmp \ --prefix="$PWD"/../install-am + - name: 'autoconf curl_config.h' + run: | + echo '::group::raw'; cat bld-am/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld-am/lib/curl_config.h | sort || true + - name: 'autoconf build' run: | make -C bld-am diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c4113337d12aef..e75536b85d4d45 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -40,30 +40,28 @@ env: # unhandled bearssl-version: 0.6 # renovate: datasource=github-tags depName=libressl-portable/portable versioning=semver registryUrl=https://github.com - libressl-version: 3.9.2 + libressl-version: 4.0.0 # renovate: datasource=github-tags depName=wolfSSL/wolfssl versioning=semver extractVersion=^v?(?.+)-stable$ registryUrl=https://github.com - wolfssl-version: 5.7.2 - # renovate: datasource=github-tags depName=ARMmbed/mbedtls versioning=semver registryUrl=https://github.com - mbedtls-version: 3.6.0 - # renovate: datasource=github-tags depName=icing/mod_h2 versioning=semver registryUrl=https://github.com - mod_h2-version: 2.0.29 + wolfssl-version: 5.7.4 + # renovate: datasource=github-tags depName=Mbed-TLS/mbedtls versioning=semver registryUrl=https://github.com + mbedtls-version: 3.6.2 # renovate: datasource=github-tags depName=nibanks/msh3 versioning=semver registryUrl=https://github.com msh3-version: 0.6.0 # renovate: datasource=github-tags depName=awslabs/aws-lc versioning=semver registryUrl=https://github.com - awslc-version: 1.36.0 + awslc-version: 1.39.0 # handled in renovate.json - openssl3-version: openssl-3.1.3 - # unhandled - quictls-version: 3.1.4+quic + openssl-version: 3.4.0 + # handled in renovate.json + quictls-version: 3.3.0 # renovate: datasource=github-tags depName=rustls/rustls-ffi versioning=semver registryUrl=https://github.com rustls-version: 0.14.0 jobs: linux: name: ${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.build.name }} - runs-on: 'ubuntu-latest' + runs-on: 'ubuntu-24.04' container: ${{ matrix.build.container }} - timeout-minutes: 90 + timeout-minutes: 45 strategy: fail-fast: false matrix: @@ -96,7 +94,7 @@ jobs: - name: wolfssl-all install_packages: zlib1g-dev install_steps: wolfssl-all - configure: LDFLAGS="-Wl,-rpath,$HOME/wolfssl-all/lib" --with-wolfssl=$HOME/wolfssl-all --enable-debug + configure: LDFLAGS="-Wl,-rpath,$HOME/wolfssl-all/lib" --with-wolfssl=$HOME/wolfssl-all --enable-httpsrr --enable-ech --enable-debug - name: wolfssl-opensslextra valgrind install_packages: zlib1g-dev valgrind @@ -113,20 +111,41 @@ jobs: install_steps: mbedtls configure: CC=clang LDFLAGS="-Wl,-rpath,$HOME/mbedtls/lib" --with-mbedtls=$HOME/mbedtls --enable-debug + - name: mbedtls + install_packages: libnghttp2-dev + install_steps: mbedtls + PKG_CONFIG_PATH: '$HOME/mbedtls/lib/pkgconfig' # Requires v3.6.0 or v2.28.8 + generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON + + - name: mbedtls-pkg + install_packages: libnghttp2-dev libmbedtls-dev + generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON + + - name: mbedtls-pkg !pc + install_packages: libnghttp2-dev libmbedtls-dev + install_steps: skipall + generate: -DCURL_USE_MBEDTLS=ON -DENABLE_DEBUG=ON -DCURL_USE_PKGCONFIG=OFF + - name: msh3 install_packages: zlib1g-dev install_steps: quictls msh3 configure: LDFLAGS="-Wl,-rpath,$HOME/msh3/lib -Wl,-rpath,$HOME/quictls/lib" --with-msh3=$HOME/msh3 --with-openssl=$HOME/quictls --enable-debug + - name: msh3 + install_packages: zlib1g-dev + install_steps: quictls msh3 skipall + PKG_CONFIG_PATH: '$HOME/msh3/lib/pkgconfig' # Broken as of v0.6.0 + generate: -DOPENSSL_ROOT_DIR=$HOME/quictls -DUSE_MSH3=ON -DMSH3_INCLUDE_DIR=$HOME/msh3/include -DMSH3_LIBRARY=$HOME/msh3/lib/libmsh3.so -DENABLE_DEBUG=ON + - name: awslc install_packages: zlib1g-dev install_steps: awslc - configure: LDFLAGS="-Wl,-rpath,$HOME/awslc/lib" --with-openssl=$HOME/awslc + configure: LDFLAGS="-Wl,-rpath,$HOME/awslc/lib" --with-openssl=$HOME/awslc --enable-httpsrr --enable-ech - name: awslc install_packages: zlib1g-dev install_steps: awslc - generate: -DOPENSSL_ROOT_DIR=$HOME/awslc -DCMAKE_UNITY_BUILD=OFF + generate: -DOPENSSL_ROOT_DIR=$HOME/awslc -DUSE_HTTPSRR=ON -DUSE_ECH=ON -DCMAKE_UNITY_BUILD=OFF - name: openssl default install_steps: pytest @@ -137,41 +156,58 @@ jobs: install_steps: pytest configure: --with-openssl --enable-debug --disable-threaded-resolver --with-libssh2 - - name: openssl3 + - name: openssl install_packages: zlib1g-dev - install_steps: gcc-11 openssl3 pytest - configure: CFLAGS=-std=gnu89 LDFLAGS="-Wl,-rpath,$HOME/openssl3/lib" --with-openssl=$HOME/openssl3 --enable-debug + install_steps: pytest + configure: CFLAGS=-std=gnu89 --with-openssl --enable-debug - - name: openssl3 -O3 valgrind + - name: openssl -O3 valgrind install_packages: zlib1g-dev valgrind - install_steps: gcc-11 openssl3 - configure: CPPFLAGS=-DCURL_WARN_SIGN_CONVERSION CFLAGS=-O3 LDFLAGS="-Wl,-rpath,$HOME/openssl3/lib" --with-openssl=$HOME/openssl3 --enable-debug + configure: CPPFLAGS=-DCURL_WARN_SIGN_CONVERSION CFLAGS=-O3 --with-openssl --enable-debug - - name: openssl3 clang krb5 + - name: openssl clang krb5 install_packages: zlib1g-dev libkrb5-dev clang - install_steps: openssl3 - configure: CC=clang LDFLAGS="-Wl,-rpath,$HOME/openssl3/lib" --with-openssl=$HOME/openssl3 --with-gssapi --enable-debug + configure: CC=clang --with-openssl --with-gssapi --enable-debug - - name: openssl3 clang krb5 + - name: openssl clang krb5 install_packages: zlib1g-dev libkrb5-dev clang - install_steps: openssl3 - generate: -DOPENSSL_ROOT_DIR=$HOME/openssl3 -DCURL_USE_GSSAPI=ON -DENABLE_DEBUG=ON + install_steps: skipall + generate: -DCURL_USE_OPENSSL=ON -DCURL_USE_GSSAPI=ON -DENABLE_DEBUG=ON - - name: openssl3 !ipv6 - install_steps: gcc-11 openssl3 - configure: LDFLAGS="-Wl,-rpath,$HOME/openssl3/lib" --with-openssl=$HOME/openssl3 --enable-debug --disable-ipv6 --disable-unity + - name: openssl !ipv6 + configure: --with-openssl --disable-ipv6 --enable-debug --disable-unity - - name: openssl3 https-only - install_steps: gcc-11 openssl3 + - name: openssl https-only configure: >- - LDFLAGS="-Wl,-rpath,$HOME/openssl3/lib" --with-openssl=$HOME/openssl3 --enable-debug --disable-unity + --with-openssl --enable-debug --disable-unity --disable-dict --disable-gopher --disable-ldap --disable-telnet --disable-imap --disable-pop3 --disable-smtp --disable-rtmp --disable-rtsp --disable-scp --disable-sftp --disable-tftp --disable-ftp --disable-file --disable-smb + - name: openssl torture !FTP + install_packages: zlib1g-dev libnghttp2-dev libssh2-1-dev libc-ares-dev + generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON + tflags: -t --shallow=25 !FTP + torture: true + + - name: openssl torture FTP + install_packages: zlib1g-dev libnghttp2-dev libssh2-1-dev libc-ares-dev + generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON + tflags: -t --shallow=20 FTP + torture: true + + - name: openssl i686 + install_packages: gcc-14-i686-linux-gnu libssl-dev:i386 librtmp-dev:i386 libssh2-1-dev:i386 libidn2-0-dev:i386 libc-ares-dev:i386 zlib1g-dev:i386 + configure: >- + PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig + CC=i686-linux-gnu-gcc-14 + CPPFLAGS=-I/usr/include/i386-linux-gnu + LDFLAGS=-L/usr/lib/i386-linux-gnu + --host=i686-linux-gnu + --with-openssl --with-librtmp --with-libssh2 --with-libidn2 --enable-ares --enable-debug + - name: '!ssl !http !smtp !imap' - install_steps: gcc-11 configure: --without-ssl --enable-debug --disable-http --disable-smtp --disable-imap --disable-unity - name: scanbuild @@ -193,12 +229,12 @@ jobs: - name: thread-sanitizer install_packages: zlib1g-dev clang libtsan2 - install_steps: pytest openssltsan3 + install_steps: pytest openssl-tsan configure: >- CC=clang CFLAGS="-fsanitize=thread -g" - LDFLAGS="-fsanitize=thread -Wl,-rpath,$HOME/openssl3/lib" - --with-openssl=$HOME/openssl3 --enable-debug + LDFLAGS="-fsanitize=thread -Wl,-rpath,$HOME/openssl/lib" + --with-openssl=$HOME/openssl --enable-debug - name: memory-sanitizer install_packages: clang @@ -212,24 +248,25 @@ jobs: - name: event-based install_packages: libssh-dev configure: --enable-debug --disable-shared --disable-threaded-resolver --with-libssh --with-openssl - tflags: -n -e '!TLS-SRP' + tflags: -n --test-event '!TLS-SRP' - - name: hyper - install_steps: rust hyper - configure: LDFLAGS="-Wl,-rpath,$HOME/hyper/target/debug" --with-openssl --with-hyper=$HOME/hyper --enable-debug + - name: duphandle + install_packages: libssh-dev + configure: --enable-debug --disable-shared --disable-threaded-resolver --with-libssh --with-openssl + tflags: -n --test-duphandle '!TLS-SRP' - name: rustls valgrind - install_packages: libpsl-dev valgrind + install_packages: valgrind install_steps: rust rustls pytest configure: --with-rustls=$HOME/rustls --enable-debug - - name: IntelC !SSL - install_packages: zlib1g-dev - install_steps: intel - configure: CC=icc --enable-debug --without-ssl + - name: rustls + install_steps: rust rustls skipall + PKG_CONFIG_PATH: '$HOME/rustls/lib/pkgconfig' # Not built as of v0.14.0 + generate: -DCURL_USE_RUSTLS=ON -DRUSTLS_INCLUDE_DIR=$HOME/rustls/include -DRUSTLS_LIBRARY=$HOME/rustls/lib/librustls.a -DENABLE_DEBUG=ON - - name: IntelC openssl valgrind - install_packages: zlib1g-dev libssl-dev valgrind + - name: IntelC openssl + install_packages: zlib1g-dev libssl-dev install_steps: intel configure: CC=icc --enable-debug --with-openssl @@ -245,23 +282,46 @@ jobs: container: 'alpine:3.18' steps: - - if: matrix.build.container == null + - if: matrix.build.container == null && !contains(matrix.build.name, 'i686') run: | sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list sudo apt-get update -y - sudo apt-get install libtool autoconf automake ninja-build pkgconf stunnel4 libpsl-dev libbrotli-dev libzstd-dev \ + sudo apt-get install -y --no-install-suggests --no-install-recommends \ + libtool autoconf automake pkgconf ninja-build stunnel4 \ + libpsl-dev libbrotli-dev libzstd-dev \ ${{ matrix.build.install_packages }} - sudo python3 -m pip install impacket - name: 'install prereqs and impacket' + python3 -m venv $HOME/venv + name: 'install prereqs' + + - if: contains(matrix.build.name, 'i686') + run: | + sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo dpkg --add-architecture i386 + sudo apt-get update -y + sudo apt-get install -y --no-install-suggests --no-install-recommends \ + libtool autoconf automake pkgconf stunnel4 \ + libpsl-dev:i386 libbrotli-dev:i386 libzstd-dev:i386 \ + ${{ matrix.build.install_packages }} + python3 -m venv $HOME/venv + name: 'install prereqs' + + - if: contains(matrix.build.install_steps, 'pytest') + run: | + sudo apt-get install -y --no-install-suggests --no-install-recommends \ + apache2 apache2-dev libnghttp2-dev vsftpd + name: 'install prereqs for pytest' - if: startsWith(matrix.build.container, 'alpine') run: | - apk add --no-cache build-base autoconf automake libtool perl openssl-dev libssh2-dev zlib-dev brotli-dev zstd-dev libidn2-dev openldap-dev heimdal-dev libpsl-dev py3-impacket py3-asn1 py3-six py3-pycryptodomex perl-time-hires openssh stunnel sudo git + apk add --no-cache build-base autoconf automake libtool perl openssl-dev libssh2-dev \ + zlib-dev brotli-dev zstd-dev libidn2-dev openldap-dev heimdal-dev libpsl-dev \ + py3-impacket py3-asn1 py3-six py3-pycryptodomex \ + perl-time-hires openssh stunnel sudo git name: 'install dependencies' - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - - name: Fix kernel mmap rnd bits + - name: 'Fix kernel mmap rnd bits' # Asan in llvm 14 provided in ubuntu 22.04 is incompatible with # high-entropy ASLR in much newer kernels that GitHub runners are # using leading to random crashes: https://reviews.llvm.org/D148280 @@ -269,20 +329,9 @@ jobs: continue-on-error: true run: sudo sysctl vm.mmap_rnd_bits=28 - - if: contains(matrix.build.install_steps, 'gcc-11') - run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - sudo add-apt-repository ppa:ubuntu-toolchain-r/ppa - sudo apt-get update - sudo apt-get install gcc-11 - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 - sudo update-alternatives --set gcc /usr/bin/gcc-11 - gcc --version - name: 'install gcc-11' - - - name: cache bearssl + - name: 'cache bearssl' if: contains(matrix.build.install_steps, 'bearssl') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-bearssl env: cache-name: cache-bearssl @@ -301,9 +350,9 @@ jobs: cp inc/*.h $HOME/bearssl/include cp build/libbearssl.* $HOME/bearssl/lib - - name: cache libressl + - name: 'cache libressl' if: contains(matrix.build.install_steps, 'libressl') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-libressl env: cache-name: cache-libressl @@ -320,9 +369,9 @@ jobs: ./configure --disable-dependency-tracking --prefix=$HOME/libressl make install - - name: cache wolfssl (all) + - name: 'cache wolfssl (all)' if: contains(matrix.build.install_steps, 'wolfssl-all') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-wolfssl-all env: cache-name: cache-wolfssl-all @@ -340,9 +389,9 @@ jobs: ./configure --disable-dependency-tracking --enable-tls13 --enable-harden --prefix=$HOME/wolfssl-all --enable-all make install - - name: cache wolfssl (opensslextra) + - name: 'cache wolfssl (opensslextra)' if: contains(matrix.build.install_steps, 'wolfssl-opensslextra') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-wolfssl-opensslextra env: cache-name: cache-wolfssl-opensslextra @@ -360,12 +409,12 @@ jobs: ./configure --disable-dependency-tracking --enable-tls13 --enable-harden --prefix=$HOME/wolfssl-opensslextra --enable-opensslextra make install - - name: cache mbedtls + - name: 'cache mbedtls' if: contains(matrix.build.install_steps, 'mbedtls') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-mbedtls env: - cache-name: cache-mbedtls + cache-name: cache-mbedtls-threadsafe with: path: /home/runner/mbedtls key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.mbedtls-version }} @@ -373,72 +422,55 @@ jobs: - name: 'build mbedtls' if: contains(matrix.build.install_steps, 'mbedtls') && steps.cache-mbedtls.outputs.cache-hit != 'true' run: | - git clone --quiet --depth=1 -b v${{ env.mbedtls-version }} https://github.com/ARMmbed/mbedtls + git clone --quiet --depth=1 -b v${{ env.mbedtls-version }} https://github.com/Mbed-TLS/mbedtls cd mbedtls - git submodule update --init - make DESTDIR=$HOME/mbedtls install - - - name: cache openssl3 - if: contains(matrix.build.install_steps, 'openssl3') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 - id: cache-openssl3 - env: - cache-name: cache-openssl3 - with: - path: /home/runner/openssl3 - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.openssl3-version }} - - - name: 'install openssl3' - if: contains(matrix.build.install_steps, 'openssl3') && steps.cache-openssl3.outputs.cache-hit != 'true' - run: | - git clone --quiet --depth=1 -b ${{ env.openssl3-version }} https://github.com/openssl/openssl - cd openssl - ./config --prefix=$HOME/openssl3 --libdir=lib - make -j1 install_sw + git submodule update --init --depth=1 + ./scripts/config.py set MBEDTLS_THREADING_C + ./scripts/config.py set MBEDTLS_THREADING_PTHREAD + cmake -B . -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_PREFIX=$HOME/mbedtls \ + -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF + cmake --build . + cmake --install . - - name: cache openssltsan3 - if: contains(matrix.build.install_steps, 'openssltsan3') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 - id: cache-openssltsan3 + - name: 'cache openssl (thread sanitizer)' + if: contains(matrix.build.install_steps, 'openssl-tsan') + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 + id: cache-openssl-tsan env: - cache-name: cache-openssltsan3 + cache-name: cache-openssl-tsan with: - path: /home/runner/openssl3 - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.openssl3-version }}-d8def798 + path: /home/runner/openssl + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.openssl-version }} - - name: 'install openssltsan3' - if: contains(matrix.build.install_steps, 'openssltsan3') && steps.cache-openssltsan3.outputs.cache-hit != 'true' - # There are global data race in openssl: - # Cherry-Pick the fix for testing https://github.com/openssl/openssl/pull/24782 + - name: 'build openssl (thread sanitizer)' + if: contains(matrix.build.install_steps, 'openssl-tsan') && steps.cache-openssl-tsan.outputs.cache-hit != 'true' run: | - git clone --quiet --depth=1 -b ${{ env.openssl3-version }} https://github.com/openssl/openssl + git clone --quiet --depth=1 -b openssl-${{ env.openssl-version }} https://github.com/openssl/openssl cd openssl - git fetch --quiet --depth=2 origin d8def79838cd0d5e7c21d217aa26edb5229f0ab4 - git cherry-pick -n d8def79838cd0d5e7c21d217aa26edb5229f0ab4 - CC="clang" CFLAGS="-fsanitize=thread" LDFLAGS="-fsanitize=thread" ./config --prefix=$HOME/openssl3 --libdir=lib + CC="clang" CFLAGS="-fsanitize=thread" LDFLAGS="-fsanitize=thread" ./config --prefix=$HOME/openssl --libdir=lib make -j1 install_sw - - name: cache quictls + - name: 'cache quictls' if: contains(matrix.build.install_steps, 'quictls') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-quictls env: cache-name: cache-quictls with: path: /home/runner/quictls - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.quictls-version }} + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.quictls-version }}-quic1 - name: 'build quictls' if: contains(matrix.build.install_steps, 'quictls') && steps.cache-quictls.outputs.cache-hit != 'true' run: | - git clone --quiet --depth=1 -b openssl-${{ env.quictls-version }} https://github.com/quictls/openssl + git clone --quiet --depth=1 -b openssl-${{ env.quictls-version }}-quic1 https://github.com/quictls/openssl cd openssl ./config --prefix=$HOME/quictls --libdir=lib make -j1 install_sw - - name: cache msh3 + - name: 'cache msh3' if: contains(matrix.build.install_steps, 'msh3') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-msh3 env: cache-name: cache-msh3 @@ -455,9 +487,9 @@ jobs: cmake --build . cmake --install . - - name: cache awslc + - name: 'cache awslc' if: contains(matrix.build.install_steps, 'awslc') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-awslc env: cache-name: cache-awslc @@ -465,7 +497,7 @@ jobs: path: /home/runner/awslc key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.awslc-version }} - - name: build awslc + - name: 'build awslc' if: contains(matrix.build.install_steps, 'awslc') && steps.cache-awslc.outputs.cache-hit != 'true' run: | curl -LOsSf --retry 6 --retry-connrefused --max-time 999 \ @@ -474,20 +506,12 @@ jobs: mkdir aws-lc-${{ env.awslc-version }}-build cd aws-lc-${{ env.awslc-version }}-build cmake -G Ninja -DCMAKE_INSTALL_PREFIX=$HOME/awslc ../aws-lc-${{ env.awslc-version }} - cmake --build . --parallel 5 + cmake --build . cmake --install . - - if: contains(matrix.build.install_steps, 'rust') - run: | - cd $HOME - curl -sSf --compressed https://sh.rustup.rs/ | sh -s -- -y - source $HOME/.cargo/env - rustup toolchain install nightly - name: 'install rust' - - - name: cache rustls + - name: 'cache rustls' if: contains(matrix.build.install_steps, 'rustls') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-rustls env: cache-name: cache-rustls @@ -495,6 +519,14 @@ jobs: path: /home/runner/rustls key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.rustls-version }} + - name: 'install rust' + if: contains(matrix.build.install_steps, 'rust') && steps.cache-rustls.outputs.cache-hit != 'true' + run: | + cd $HOME + curl -sSf --compressed https://sh.rustup.rs/ | sh -s -- -y + source $HOME/.cargo/env + rustup toolchain install nightly + - name: 'build rustls' if: contains(matrix.build.install_steps, 'rustls') && steps.cache-rustls.outputs.cache-hit != 'true' run: | @@ -502,15 +534,6 @@ jobs: cd rustls-ffi make DESTDIR=$HOME/rustls install - - if: contains(matrix.build.install_steps, 'hyper') - run: | - cd $HOME - git clone --quiet --depth=1 https://github.com/hyperium/hyper.git - cd $HOME/hyper - RUSTFLAGS="--cfg hyper_unstable_ffi" cargo +nightly rustc --features client,http1,http2,ffi -Z unstable-options --crate-type cdylib - echo "LD_LIBRARY_PATH=$HOME/hyper/target/debug:/usr/local/lib" >> $GITHUB_ENV - name: 'install hyper' - - if: contains(matrix.build.install_steps, 'intel') run: | cd /tmp @@ -521,38 +544,6 @@ jobs: printenv >> $GITHUB_ENV name: 'install Intel compilers' - - if: contains(matrix.build.install_steps, 'pytest') - run: | - sudo apt-get install apache2 apache2-dev libnghttp2-dev vsftpd - sudo python3 -m pip install -r tests/http/requirements.txt - name: 'install pytest and apach2-dev' - - - name: cache mod_h2 - if: contains(matrix.build.install_steps, 'pytest') - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 - id: cache-mod_h2 - env: - cache-name: cache-mod_h2 - with: - path: /home/runner/mod_h2 - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.mod_h2-version }} - - - name: 'build mod_h2' - if: contains(matrix.build.install_steps, 'pytest') && steps.cache-mod_h2.outputs.cache-hit != 'true' - run: | - cd $HOME - git clone --quiet --depth=1 -b v${{ env.mod_h2-version }} https://github.com/icing/mod_h2 - cd mod_h2 - autoreconf -fi - ./configure - make - - - name: 'install mod_h2' - if: contains(matrix.build.install_steps, 'pytest') - run: | - cd $HOME/mod_h2 - sudo make install - - run: autoreconf -fi if: ${{ matrix.build.configure }} name: 'autoreconf' @@ -560,8 +551,11 @@ jobs: - name: 'configure' run: | [[ '${{ matrix.build.install_steps }}' = *'awslc'* ]] && sudo apt remove --yes libssl-dev + if [ -n '${{ matrix.build.PKG_CONFIG_PATH }}' ]; then + export PKG_CONFIG_PATH="${{ matrix.build.PKG_CONFIG_PATH }}" + fi if [ -n '${{ matrix.build.generate }}' ]; then - cmake -G Ninja \ + cmake -B . -G Ninja \ -DCMAKE_C_COMPILER_TARGET=$(uname -m)-pc-linux-gnu -DBUILD_STATIC_LIBS=ON \ -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \ -DCURL_BROTLI=ON -DCURL_ZSTD=ON \ @@ -576,6 +570,11 @@ jobs: if: ${{ !cancelled() }} run: cat config.log CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true + - name: 'curl_config.h' + run: | + echo '::group::raw'; cat lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' lib/curl_config.h | sort || true + - name: 'test configs' run: | cat tests/config || true @@ -584,12 +583,12 @@ jobs: - name: 'build' run: | if [ -n '${{ matrix.build.generate }}' ]; then - ${{ matrix.build.make-prefix }} cmake --build . --parallel 5 --verbose + ${{ matrix.build.make-prefix }} cmake --build . --verbose else ${{ matrix.build.make-prefix }} make V=1 fi - - name: single-use function check + - name: 'single-use function check' if: ${{ contains(matrix.build.configure, '--disable-unity') || contains(matrix.build.generate, '-DCMAKE_UNITY_BUILD=OFF') }} run: | git config --global --add safe.directory "*" @@ -611,34 +610,50 @@ jobs: if: ${{ matrix.build.install_steps != 'skipall' }} run: | if [ -n '${{ matrix.build.generate }}' ]; then - cmake --build . --parallel 5 --verbose --target testdeps + cmake --build . --verbose --target testdeps else make V=1 -C tests fi + - name: 'install test prereqs' + if: ${{ matrix.build.install_steps != 'skipall' && matrix.build.container == null }} + run: | + [ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate + python3 -m pip install -r tests/requirements.txt + - name: 'run tests' if: ${{ matrix.build.install_steps != 'skipall' && matrix.build.install_steps != 'skiprun' }} timeout-minutes: ${{ contains(matrix.build.install_packages, 'valgrind') && 30 || 15 }} run: | export TFLAGS='${{ matrix.build.tflags }}' - if [[ '${{ matrix.build.install_packages }}' = *'valgrind'* ]]; then - TFLAGS+=' -j6' - fi - if [[ '${{ matrix.build.install_packages }}' = *'heimdal-dev'* ]]; then - TFLAGS+=' ~2077 ~2078' # valgrind errors + if [ -z '${{ matrix.build.torture }}' ]; then + if [[ '${{ matrix.build.install_packages }}' = *'valgrind'* ]]; then + TFLAGS+=' -j6' + fi + if [[ '${{ matrix.build.install_packages }}' = *'heimdal-dev'* ]]; then + TFLAGS+=' ~2077 ~2078' # valgrind errors + fi fi + [ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate if [ -n '${{ matrix.build.generate }}' ]; then - cmake --build . --verbose --target test-ci + cmake --build . --verbose --target ${{ matrix.build.torture && 'test-torture' || 'test-ci' }} else - make V=1 test-ci + make V=1 ${{ matrix.build.torture && 'test-torture' || 'test-ci' }} fi + - name: 'install pytest prereqs' + if: contains(matrix.build.install_steps, 'pytest') + run: | + [ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate + python3 -m pip install -r tests/http/requirements.txt + - name: 'run pytest' if: contains(matrix.build.install_steps, 'pytest') env: TFLAGS: "${{ matrix.build.tflags }}" CURL_CI: github run: | + [ -x "$HOME/venv/bin/activate" ] && source $HOME/venv/bin/activate if [ -n '${{ matrix.build.generate }}' ]; then cmake --build . --verbose --target curl-pytest-ci else @@ -648,7 +663,7 @@ jobs: - name: 'build examples' run: | if [ -n '${{ matrix.build.generate }}' ]; then - ${{ matrix.build.make-prefix }} cmake --build . --parallel 5 --verbose --target curl-examples + ${{ matrix.build.make-prefix }} cmake --build . --verbose --target curl-examples else ${{ matrix.build.make-prefix }} make V=1 examples fi diff --git a/.github/workflows/linux32.yml b/.github/workflows/linux32.yml deleted file mode 100644 index 9e8ce07b6ca7c6..00000000000000 --- a/.github/workflows/linux32.yml +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright (C) Dan Fandrich -# -# SPDX-License-Identifier: curl - -name: Linux 32-bit - -'on': - push: - branches: - - master - - '*/ci' - paths-ignore: - - '**/*.md' - - '**/CMakeLists.txt' - - '.circleci/**' - - 'appveyor.*' - - 'CMake/**' - - 'packages/**' - - 'plan9/**' - - 'projects/**' - - 'winbuild/**' - pull_request: - branches: - - master - paths-ignore: - - '**/*.md' - - '**/CMakeLists.txt' - - '.circleci/**' - - 'appveyor.*' - - 'CMake/**' - - 'packages/**' - - 'plan9/**' - - 'projects/**' - - 'winbuild/**' - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} - cancel-in-progress: true - -permissions: {} - -env: - MAKEFLAGS: -j 5 - -jobs: - linux-i686: - name: ${{ matrix.build.name }} - runs-on: 'ubuntu-24.04' - timeout-minutes: 90 - strategy: - fail-fast: false - matrix: - build: - - name: Linux i686 - install_packages: gcc-14-i686-linux-gnu libssl-dev:i386 librtmp-dev:i386 libssh2-1-dev:i386 libidn2-0-dev:i386 libc-ares-dev:i386 zlib1g-dev:i386 libpsl-dev:i386 libbrotli-dev:i386 libzstd-dev:i386 - configure: --enable-debug --with-openssl --with-librtmp --with-libssh2 --with-libidn2 --enable-ares --host=i686-linux-gnu CC=i686-linux-gnu-gcc-14 PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig CPPFLAGS=-I/usr/include/i386-linux-gnu LDFLAGS=-L/usr/lib/i386-linux-gnu - - steps: - - run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - sudo dpkg --add-architecture i386 - sudo apt-get update -y - sudo apt-get install -y --no-install-suggests --no-install-recommends libtool autoconf automake pkgconf stunnel4 ${{ matrix.build.install_packages }} - sudo python3 -m pip install --break-system-packages impacket - name: 'install prereqs' - - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 - - - run: autoreconf -fi - name: 'autoreconf' - - - run: | - ./configure --disable-dependency-tracking --enable-unity --enable-test-bundles --enable-warnings --enable-werror \ - ${{ matrix.build.configure }} - name: 'configure' - - - run: make V=1 - name: 'make' - - - run: ./src/curl -V - name: 'check curl -V output' - - - run: make V=1 -C tests - name: 'make tests' - - - run: make V=1 test-ci - name: 'run tests' - env: - TFLAGS: "${{ matrix.build.tflags }}" - - - run: make V=1 examples - name: 'make examples' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 75af2fba59c9ad..8669f978bbe4b0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -54,18 +54,20 @@ env: MAKEFLAGS: -j 4 jobs: - autotools: - name: 'AM ${{ matrix.compiler }} ${{ matrix.name }}' + macos: + name: "${{ matrix.build.generate && 'CM' || 'AM' }} ${{ matrix.compiler }} ${{ matrix.build.name }}" runs-on: 'macos-latest' - timeout-minutes: 60 + timeout-minutes: 45 env: - DEVELOPER_DIR: "/Applications/Xcode${{ matrix.xcode && format('_{0}', matrix.xcode) || '' }}.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode${{ matrix.build.xcode && format('_{0}', matrix.build.xcode) || '' }}.app/Contents/Developer" CC: ${{ matrix.compiler }} - CFLAGS: '-mmacosx-version-min=${{ matrix.macos-version-min }}' + CFLAGS: '' strategy: fail-fast: false matrix: - include: + compiler: [clang, llvm@15, gcc-12] + build: + # automake - name: '!ssl !debug brotli zstd' compiler: clang install: brotli zstd @@ -99,12 +101,12 @@ jobs: macos-version-min: '10.9' - name: '!ssl HTTP-only' compiler: clang - configure: | - --enable-debug \ - --disable-alt-svc --disable-dict --disable-file --disable-ftp --disable-gopher --disable-imap \ - --disable-ldap --disable-pop3 --disable-rtmp --disable-rtsp --disable-scp --disable-sftp \ - --disable-shared --disable-smb --disable-smtp --disable-telnet --disable-tftp --disable-unix-sockets \ - --without-brotli --without-gssapi --without-libidn2 --without-libpsl --without-librtmp --without-libssh2 \ + configure: >- + --enable-debug + --disable-alt-svc --disable-dict --disable-file --disable-ftp --disable-gopher --disable-imap + --disable-ldap --disable-pop3 --disable-rtmp --disable-rtsp --disable-scp --disable-sftp + --disable-shared --disable-smb --disable-smtp --disable-telnet --disable-tftp --disable-unix-sockets + --without-brotli --without-gssapi --without-libidn2 --without-libpsl --without-librtmp --without-libssh2 --without-nghttp2 --without-ntlm-auth --without-ssl --without-zlib --without-zstd macos-version-min: '10.15' # Catalina (2019) @@ -134,149 +136,21 @@ jobs: compiler: clang configure: --enable-debug --with-openssl=$(brew --prefix openssl) macos-version-min: '10.9' - tflags: -e + tflags: --test-event - name: 'OpenSSL libssh2 !ldap 10.15' compiler: clang configure: --enable-debug --disable-ldap --with-openssl=$(brew --prefix openssl) macos-version-min: '10.15' - steps: - - name: 'brew install' - # Run this command with retries because of spurious failures seen - # while running the tests, for example - # https://github.com/curl/curl/runs/4095721123?check_suite_focus=true - run: | - echo automake libtool pkg-config libpsl libssh2 nghttp2 stunnel ${{ matrix.install }} | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile - while [[ $? == 0 ]]; do for i in 1 2 3; do brew update && brew bundle install --no-lock --file /tmp/Brewfile && break 2 || { echo Error: wait to try again; sleep 10; } done; false Too many retries; done - - - name: 'brew unlink openssl' - run: | - if test -d $(brew --prefix)/include/openssl; then - brew unlink openssl - fi - - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 - - - name: 'toolchain versions' - run: | - [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang" - [[ '${{ matrix.compiler }}' = 'gcc'* ]] && \ - grep -h -r -E -o '.+[0-9.]+\.sdk/' "$(dirname "$("${CC}" -print-libgcc-file-name)")/include-fixed" | sed -E 's/^\t+//g' | tr -d '"' | sort -u || true - which "${CC}"; "${CC}" --version || true - xcodebuild -version || true - xcrun --sdk macosx --show-sdk-path 2>/dev/null || true - xcrun --sdk macosx --show-sdk-version || true - echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' - echo '::group::brew packages installed'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::' - - - name: 'autoreconf' - run: autoreconf -fi - - - name: 'configure' - run: | - if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then - CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang" - CC+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" - CC+=" --target=$(uname -m)-apple-darwin" - options+=" --target=$(uname -m)-apple-darwin" - fi - CFLAGS+=' ${{ matrix.cflags }}' - if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then - libgccdir="$(dirname "$("${CC}" -print-libgcc-file-name)")" - echo '::group::gcc include-fixed details'; find "${libgccdir}/include-fixed" | sort; echo '::endgroup::' - for f in dispatch os AvailabilityInternal.h stdio.h; do - if [ -r "${libgccdir}/include-fixed/${f}" ]; then - echo "Zap gcc hack: '${libgccdir}/include-fixed/${f}'" - mv "${libgccdir}/include-fixed/${f}" "${libgccdir}/include-fixed/${f}-BAK" - fi - done - fi - if [ '${{ matrix.compiler }}' != 'clang' ]; then - options+=" --with-sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" - CFLAGS+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" - fi - mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-warnings --enable-werror \ - --disable-dependency-tracking \ - --with-libpsl=$(brew --prefix libpsl) \ - ${{ matrix.configure }} ${options} - - - name: 'configure log' - if: ${{ !cancelled() }} - run: cat bld/config.log || true - - - name: 'curl_config.h' - run: | - echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' - cat bld/lib/curl_config.h | grep -F '#define' | sort || true - - - name: 'build-cert' - if: contains(matrix.configure, '--with-secure-transport') - run: | - make -C bld/tests/certs clean-certs - make -C bld/tests/certs build-certs -j1 - - - name: 'make' - run: make -C bld V=1 - - - name: 'curl version' - run: bld/src/curl --disable --version - - - name: 'make tests' - run: make -C bld V=1 -C tests - - - name: 'pip3 install' - run: | - python3 -m venv $HOME/venv - source $HOME/venv/bin/activate - python3 -m pip install impacket - - - name: 'run tests' - timeout-minutes: 20 - run: | - export TFLAGS='-j20 ${{ matrix.tflags }}' - TFLAGS+=' ~2037 ~2041' # flaky - if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then - TFLAGS+=' ~RTSP' # 567 568 569 570 571 572 577 689 3100 - TFLAGS+=' ~1156 ~1539' # HTTP Content-Range, Content-Length - if [[ '${{ matrix.configure }}' = *'--with-secure-transport'* ]]; then - TFLAGS+=' ~2100' # 2100:'HTTP GET using DoH' https://github.com/curl/curl/actions/runs/9942146678/job/27462937524#step:15:5059 - TFLAGS+=' ~HTTP/2' # 2400 2401 2402 2403 2404 2406, Secure Transport + nghttp2 - else - TFLAGS+=' ~2402 ~2404' # non-Secure Transport + nghttp2 - fi - fi - if [[ '${{ matrix.configure }}' = *'--with-secure-transport'* ]]; then - TFLAGS+=' ~313' # Secure Transport does not support crl file - TFLAGS+=' ~1631 ~1632' # Secure Transport is not able to shutdown ftp over https gracefully yet - fi - source $HOME/venv/bin/activate - rm -f $HOME/.curlrc - make -C bld V=1 test-ci - - - name: 'make examples' - if: ${{ contains(matrix.build.name, '+examples') }} - run: make -C bld V=1 examples - - cmake: - name: 'CM ${{ matrix.compiler }} ${{ matrix.build.name }}' - runs-on: 'macos-latest' - timeout-minutes: 30 - env: - DEVELOPER_DIR: "/Applications/Xcode${{ matrix.xcode && format('_{0}', matrix.xcode) || '' }}.app/Contents/Developer" - CC: ${{ matrix.compiler }} - strategy: - fail-fast: false - matrix: - compiler: [clang, llvm@15, gcc-12] - build: - - name: 'OpenSSL ws gsasl AppleIDN' + # cmake + - name: 'OpenSSL gsasl AppleIDN' install: gsasl generate: -DOPENSSL_ROOT_DIR=$(brew --prefix openssl) -DCURL_USE_GSASL=ON -DUSE_APPLE_IDN=ON macos-version-min: '10.9' - - name: 'OpenSSL +static libssh' + - name: 'OpenSSL +static libssh +examples' install: libssh generate: -DOPENSSL_ROOT_DIR=$(brew --prefix openssl) -DBUILD_STATIC_LIBS=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON macos-version-min: '10.9' - - name: 'SecureTransport ws debug' + - name: 'SecureTransport debug' generate: -DCURL_USE_SECTRANSP=ON -DENABLE_DEBUG=ON macos-version-min: '10.8' - name: 'LibreSSL !ldap heimdal c-ares +examples' @@ -285,7 +159,11 @@ jobs: macos-version-min: '10.15' - name: 'wolfSSL !ldap brotli zstd' install: brotli wolfssl zstd - generate: -DCURL_USE_WOLFSSL=ON -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_DISABLE_LDAP=ON + generate: -DCURL_USE_WOLFSSL=ON -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_DISABLE_LDAP=ON -DUSE_HTTPSRR=ON -DUSE_ECH=ON + macos-version-min: '10.15' + - name: 'mbedTLS !ldap brotli zstd' + install: brotli mbedtls zstd + generate: -DCURL_USE_MBEDTLS=ON -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_DISABLE_LDAP=ON macos-version-min: '10.15' - name: 'GnuTLS !ldap krb5' install: gnutls nettle krb5 @@ -303,12 +181,24 @@ jobs: torture: true exclude: - { compiler: llvm@15, build: { macos-version-min: '10.15' } } - - { compiler: llvm@15, build: { macos-version-min: '10.9' } } + - { compiler: llvm@15, build: { torture: true } } - { compiler: gcc-12, build: { torture: true } } + # opt out jobs from combinations that have the compiler set manually + - { compiler: llvm@15, build: { compiler: 'clang' } } + - { compiler: llvm@15, build: { compiler: 'gcc-12' } } + - { compiler: gcc-12, build: { compiler: 'clang' } } + - { compiler: gcc-12, build: { compiler: 'llvm@15' } } + - { compiler: clang, build: { compiler: 'gcc-12' } } + - { compiler: clang, build: { compiler: 'llvm@15' } } + steps: - name: 'brew install' + # Run this command with retries because of spurious failures seen + # while running the tests, for example + # https://github.com/curl/curl/runs/4095721123?check_suite_focus=true run: | - echo ninja pkg-config libpsl libssh2 nghttp2 stunnel ${{ matrix.build.install }} | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile + echo ${{ matrix.build.generate && 'ninja' || 'automake libtool' }} \ + pkgconf libpsl libssh2 nghttp2 stunnel ${{ matrix.build.install }} | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile while [[ $? == 0 ]]; do for i in 1 2 3; do brew update && brew bundle install --no-lock --file /tmp/Brewfile && break 2 || { echo Error: wait to try again; sleep 10; } done; false Too many retries; done - name: 'brew unlink openssl' @@ -317,74 +207,107 @@ jobs: brew unlink openssl fi - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'toolchain versions' run: | [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang" - [[ '${{ matrix.compiler }}' = 'gcc'* ]] && \ - grep -h -r -E -o '.+[0-9.]+\.sdk/' "$(dirname "$("${CC}" -print-libgcc-file-name)")/include-fixed" | sed -E 's/^\t+//g' | tr -d '"' | sort -u || true + [[ '${{ matrix.compiler }}' = 'gcc'* ]] && "${CC}" --print-sysroot which "${CC}"; "${CC}" --version || true xcodebuild -version || true xcrun --sdk macosx --show-sdk-path 2>/dev/null || true xcrun --sdk macosx --show-sdk-version || true + ls -l /Library/Developer/CommandLineTools/SDKs || true echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' echo '::group::brew packages installed'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::' - - name: 'cmake configure' + - name: 'autoreconf' + if: ${{ matrix.build.configure }} + run: autoreconf -fi + + - name: 'configure' run: | + if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then + sysroot="$("${CC}" --print-sysroot)" # Must match the SDK gcc was built for + else + sysroot="$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" + fi + if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang" - CC+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" + CC+=" --sysroot=${sysroot}" CC+=" --target=$(uname -m)-apple-darwin" fi - if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then - libgccdir="$(dirname "$("${CC}" -print-libgcc-file-name)")" - echo '::group::gcc include-fixed details'; find "${libgccdir}/include-fixed" | sort; echo '::endgroup::' - for f in dispatch os AvailabilityInternal.h stdio.h; do - if [ -r "${libgccdir}/include-fixed/${f}" ]; then - echo "Zap gcc hack: '${libgccdir}/include-fixed/${f}'" - mv "${libgccdir}/include-fixed/${f}" "${libgccdir}/include-fixed/${f}-BAK" - fi - done + + if [ -n '${{ matrix.build.configure }}' ]; then + export CFLAGS + if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then + options+=" --target=$(uname -m)-apple-darwin" + fi + if [ '${{ matrix.compiler }}' != 'clang' ]; then + options+=" --with-sysroot=${sysroot}" + CFLAGS+=" --sysroot=${sysroot}" + fi + CFLAGS+=' ${{ matrix.build.cflags }}' + CFLAGS+=' -mmacosx-version-min=${{ matrix.build.macos-version-min }}' + mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-warnings --enable-werror \ + --disable-dependency-tracking \ + --with-libpsl=$(brew --prefix libpsl) \ + ${{ matrix.build.configure }} ${options} + else + cmake -B bld -G Ninja -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.build.macos-version-min }} \ + "-DCMAKE_OSX_SYSROOT=${sysroot}" \ + "-DCMAKE_C_COMPILER_TARGET=$(uname -m | sed 's/arm64/aarch64/')-apple-darwin$(uname -r)" \ + ${{ matrix.build.generate }} fi - cmake -B bld -G Ninja -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.build.macos-version-min }} \ - "-DCMAKE_OSX_SYSROOT=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" \ - "-DCMAKE_C_COMPILER_TARGET=$(uname -m | sed 's/arm64/aarch64/')-apple-darwin$(uname -r)" \ - ${{ matrix.build.generate }} - name: 'configure log' if: ${{ !cancelled() }} - run: cat bld/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true + 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::' - cat bld/lib/curl_config.h | grep -F '#define' | sort || true + grep -F '#define' bld/lib/curl_config.h | sort || true - name: 'build-cert' - if: contains(matrix.build.generate, '-DCURL_USE_SECTRANSP=ON') + if: contains(matrix.build.generate, '-DCURL_USE_SECTRANSP=ON') || contains(matrix.build.configure, '--with-secure-transport') run: | - ninja -C bld clean-certs - ninja -C bld build-certs -j 1 + if [ -n '${{ matrix.build.configure }}' ]; then + make -C bld/tests/certs clean-certs + make -C bld/tests/certs build-certs -j1 + else + cmake --build bld --target clean-certs + cmake --build bld --target build-certs --parallel 1 + fi - - name: 'cmake build' - run: ninja -C bld --verbose + - name: 'build' + run: | + if [ -n '${{ matrix.build.configure }}' ]; then + make -C bld V=1 + else + cmake --build bld --verbose + fi - name: 'curl version' run: bld/src/curl --disable --version - - name: 'cmake build tests' - run: ninja -C bld testdeps + - name: 'build tests' + run: | + if [ -n '${{ matrix.build.configure }}' ]; then + make -C bld V=1 -C tests + else + cmake --build bld --target testdeps + fi - - name: 'pip3 install' + - name: 'install test prereqs' run: | python3 -m venv $HOME/venv source $HOME/venv/bin/activate - python3 -m pip install impacket + python3 -m pip install -r tests/requirements.txt - - name: 'cmake run tests' + - name: 'run tests' timeout-minutes: ${{ matrix.build.torture && 20 || 10 }} run: | export TFLAGS='-j20 ${{ matrix.build.tflags }}' @@ -393,25 +316,39 @@ jobs: if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then TFLAGS+=' ~RTSP' # 567 568 569 570 571 572 577 689 3100 TFLAGS+=' ~1156 ~1539' # HTTP Content-Range, Content-Length - if [[ '${{ matrix.build.generate }}' = *'-DCURL_USE_SECTRANSP=ON'* ]]; then + if [[ -n '${{ matrix.build.configure }}' || \ + '${{ matrix.build.generate }}' = *'-DCURL_USE_SECTRANSP=ON'* ]]; then TFLAGS+=' ~2100' # 2100:'HTTP GET using DoH' https://github.com/curl/curl/actions/runs/9942146678/job/27462937524#step:15:5059 + fi + if [[ '${{ matrix.build.configure }}' = *'--with-secure-transport'* || \ + '${{ matrix.build.generate }}' = *'-DCURL_USE_SECTRANSP=ON'* ]]; then TFLAGS+=' ~HTTP/2' # 2400 2401 2402 2403 2404 2406, Secure Transport + nghttp2 else TFLAGS+=' ~2402 ~2404' # non-Secure Transport + nghttp2 fi fi - if [[ '${{ matrix.build.generate }}' = *'-DCURL_USE_SECTRANSP=ON'* ]]; then + if [[ '${{ matrix.build.configure }}' = *'--with-secure-transport'* || \ + '${{ matrix.build.generate }}' = *'-DCURL_USE_SECTRANSP=ON'* ]]; then TFLAGS+=' ~313' # Secure Transport does not support crl file TFLAGS+=' ~1631 ~1632' # Secure Transport is not able to shutdown ftp over https gracefully yet fi fi source $HOME/venv/bin/activate rm -f $HOME/.curlrc - ninja -C bld test-ci + if [ -n '${{ matrix.build.configure }}' ]; then + make -C bld V=1 ${{ matrix.build.torture && 'test-torture' || 'test-ci' }} + else + cmake --build bld --target ${{ matrix.build.torture && 'test-torture' || 'test-ci' }} + fi - - name: 'cmake build examples' - if: ${{ contains(matrix.name, '+examples') }} - run: make -C bld VERBOSE=1 + - name: 'build examples' + if: ${{ contains(matrix.build.name, '+examples') }} + run: | + if [ -n '${{ matrix.build.configure }}' ]; then + make -C bld examples V=1 + else + cmake --build bld --target curl-examples --verbose + fi combinations: # Test buildability with host OS, Xcode / SDK, compiler, target-OS, Secure Transport/not, built tool, combinations if: true # Set to `true` to enable this test matrix. It runs quickly. @@ -470,8 +407,7 @@ jobs: # Reduce build combinations, by dropping less interesting ones - { compiler: gcc-12, config: SecureTransport } - { compiler: gcc-13, build: cmake } - - { compiler: gcc-13, image: macos-13 } - - { compiler: gcc-14, config: SecureTransport } + - { compiler: gcc-14, build: autotools } steps: - name: 'install autotools' if: ${{ matrix.build == 'autotools' }} @@ -479,17 +415,17 @@ jobs: echo automake libtool | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile while [[ $? == 0 ]]; do for i in 1 2 3; do brew update && brew bundle install --no-lock --file /tmp/Brewfile && break 2 || { echo Error: wait to try again; sleep 10; } done; false Too many retries; done - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'toolchain versions' run: | [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang" - [[ '${{ matrix.compiler }}' = 'gcc'* ]] && \ - grep -h -r -E -o '.+[0-9.]+\.sdk/' "$(dirname "$("${CC}" -print-libgcc-file-name)")/include-fixed" | sed -E 's/^\t+//g' | tr -d '"' | sort -u || true + [[ '${{ matrix.compiler }}' = 'gcc'* ]] && "${CC}" --print-sysroot which "${CC}"; "${CC}" --version || true xcodebuild -version || true xcrun --sdk macosx --show-sdk-path 2>/dev/null || true xcrun --sdk macosx --show-sdk-version || true + ls -l /Library/Developer/CommandLineTools/SDKs || true echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::' echo '::group::brew packages preinstalled'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::' @@ -499,115 +435,26 @@ jobs: - name: 'configure / ${{ matrix.build }}' run: | + if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then + sysroot="$("${CC}" --print-sysroot)" # Must match the SDK gcc was built for + else + sysroot="$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" + fi + if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang" - CC+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" + CC+=" --sysroot=${sysroot}" CC+=" --target=$(uname -m)-apple-darwin" fi - # gcc ships with an `include-fixed` header set, which overrides SDK - # headers with the intent of making them compatible with gcc. The - # source for these headers is: - # https://github.com/gcc-mirror/gcc/tree/master/fixincludes - # with extra Apple-specific patches applied from here for Homebrew: - # https://github.com/iains/gcc-12-branch - # - # They pass through a generator phase at build-time which seems to - # pick the SDK installed on the build machine (maintained by the - # Homebrew project in our case) and patches it according to a set - # of rules in `inclhack.def`. - # - # Homebrew builds and ships different binaries for different macOS - # versions and CPUs, built on machines using the same OS version as - # the target one. Each of these machines have a particular version - # of Apple CommandLineTools with a default SDK version installed with - # them. - # - # Then this binary gets installed onto the end-user machine, - # matching the OS version at the time of installation. - # - # The problem with this approach is that the SDK version picked up - # at gcc build-time has a high chance of being or becoming out of - # sync with actual SDK installed on the end-user machine. This - # can happen after upgrading the OS, Xcode, selecting an SDK version - # manually, or other reasons. - # - # When the SDK versions do not match, the gcc hacks, instead of - # improving compatibility the SDK, are actively _breaking_ - # compatibility, in an unexpected, hard to diagnose way. - # - # The SDK version used for gcc-hacks is not advertised. We can - # extract the major SDK version from the generated gcc-hack header - # files, assuming someone knows what to look for and where. - # - # Basically it also means that the same `gcc-N` Homebrew package - # behaves differently depending on the OS it was built on. Causing - # an explosion of build combination. It may also mean that a minor - # gcc version bump is built against a different SDK version, and due - # to the extra patch for the hack applied by Homebrew, there may - # be extra changes as well. - # - # For GHA runners, it means that the default Xcode + OS combo is - # broken in 8 out of 12 combinations (66%) have an SDK mismatch, - # and 9 fail to build (75%). These are the 3 lucky default - # combinations that worked to build curl: - # macos-14 + Xcode 15.0.1 + gcc-12, gcc-14 - # - # Of all possible valid GHA runner, gcc, manually selected Xcode - # combinations, 40% are broken. - # - # Compared to mainline llvm: llvm ships the same binaries regardless - # of build-OS or environent, it contains no SDK-version-specific - # hacks, and has no 3rd party patches. This still leaves some - # occasional issues, but works much closer to expectations. - # - # Some of these hacks are helpful, in particular for fixing this - # issue via math.h: - # /Applications/Xcode_14.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/math.h:53:5: error: #error "Unsupported value of - # 53 | # error "Unsupported value of __FLT_EVAL_METHOD__." - # - # Errors seen in available CI combinations: - # error: two or more data types in declaration specifiers # fatal error: AvailabilityInternalLegacy.h: No such file or directory - # gcc-13 + macos-14 + Xcode 14.3.1 - # error: two or more data types in declaration specifiers - # gcc-13 + macos-12 + Xcode 14.1, 14.2 - # gcc-13 + Xcode 15.0.1, 15.1, 5.2 - # error: expected ';' before 'extern' - # gcc-12, gcc-14 + macos-12 + Xcode 14.1, 14.2 - # error: unknown type name 'dispatch_queue_t' - # gcc-12 + macos-13 + Xcode 15.0.1, 15.1, 15.2 - # error: type defaults to 'int' in declaration of 'DISPATCH_DECL_FACTORY_CLASS_SWIFT' [-Wimplicit-int] - # gcc-14 macos-13 Xcode 15.0.1, 15.1, 15.2 - # error: unknown type name 'FILE' - # Xcode 16.0 - # - # Unbreak Homebrew gcc builds by moving problematic SDK header overlay - # directories/files out of the way: - if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then - # E.g.: - # $(brew --prefix)/Cellar/gcc@11/11.4.0/lib/gcc/11/gcc/aarch64-apple-darwin23/11/include-fixed - # $(brew --prefix)/Cellar/gcc@11/11.4.0/lib/gcc/11/gcc/x86_64-apple-darwin21/11/include-fixed - # $(brew --prefix)/Cellar/gcc/14.1.0_1/lib/gcc/14/gcc/x86_64-apple-darwin21/14/include-fixed - libgccdir="$(dirname "$("${CC}" -print-libgcc-file-name)")" - echo '::group::gcc include-fixed details'; find "${libgccdir}/include-fixed" | sort; echo '::endgroup::' - patch_out='dispatch os AvailabilityInternal.h' - patch_out+=' stdio.h' # for Xcode 16 error: unknown type name 'FILE' - for f in ${patch_out}; do - if [ -r "${libgccdir}/include-fixed/${f}" ]; then - echo "Zap gcc hack: '${libgccdir}/include-fixed/${f}'" - mv "${libgccdir}/include-fixed/${f}" "${libgccdir}/include-fixed/${f}-BAK" - fi - done - fi - if [ '${{ matrix.build }}' = 'autotools' ]; then export CFLAGS if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then options+=" --target=$(uname -m)-apple-darwin" fi if [ '${{ matrix.compiler }}' != 'clang' ]; then - options+=" --with-sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" - CFLAGS+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" + options+=" --with-sysroot=${sysroot}" + CFLAGS+=" --sysroot=${sysroot}" fi [ '${{ matrix.config }}' = 'OpenSSL' ] && options+=" --with-openssl=$(brew --prefix openssl)" [ '${{ matrix.config }}' = 'SecureTransport' ] && options+=' --with-secure-transport' @@ -625,7 +472,7 @@ jobs: # would pick up nghttp2, libidn2, and libssh2 cmake -B bld -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.macos-version-min }} \ - "-DCMAKE_OSX_SYSROOT=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" \ + "-DCMAKE_OSX_SYSROOT=${sysroot}" \ "-DCMAKE_C_COMPILER_TARGET=$(uname -m | sed 's/arm64/aarch64/')-apple-darwin$(uname -r)" \ "-DCMAKE_IGNORE_PREFIX_PATH=$(brew --prefix)" \ -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF \ @@ -641,7 +488,7 @@ jobs: - name: 'curl_config.h' run: | echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' - cat bld/lib/curl_config.h | grep -F '#define' | sort || true + grep -F '#define' bld/lib/curl_config.h | sort || true - name: 'build / ${{ matrix.build }}' run: make -C bld V=1 VERBOSE=1 diff --git a/.github/workflows/non-native.yml b/.github/workflows/non-native.yml index c8b4601e3eab95..108f515afd8944 100644 --- a/.github/workflows/non-native.yml +++ b/.github/workflows/non-native.yml @@ -44,7 +44,7 @@ jobs: matrix: arch: ['x86_64'] steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'cmake' uses: cross-platform-actions/action@cdc9ee69ef84a5f2e59c9058335d9c57bcb4ac86 # v0.25.0 with: @@ -61,15 +61,17 @@ jobs: -DCURL_USE_OPENSSL=ON \ -DCURL_BROTLI=ON -DCURL_USE_GSSAPI=ON \ || { cat bld/CMakeFiles/CMake*.yaml; false; } - cmake --build bld --config Debug --parallel 3 + 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::' + cmake --build bld --config Debug bld/src/curl --disable --version if [ '${{ matrix.arch }}' = 'x86_64' ]; then # Slow on emulated CPU - cmake --build bld --config Debug --parallel 3 --target testdeps + cmake --build bld --config Debug --target testdeps export TFLAGS='-j4' cmake --build bld --config Debug --target test-ci fi echo '::group::build examples' - cmake --build bld --config Debug --parallel 3 --target curl-examples + cmake --build bld --config Debug --target curl-examples echo '::endgroup::' openbsd: @@ -80,7 +82,7 @@ jobs: matrix: arch: ['x86_64'] steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'cmake' uses: cross-platform-actions/action@cdc9ee69ef84a5f2e59c9058335d9c57bcb4ac86 # v0.25.0 with: @@ -89,7 +91,8 @@ jobs: architecture: ${{ matrix.arch }} run: | # https://openbsd.app/ - sudo pkg_add cmake ninja perl brotli openldap-client libssh2 libidn2 libpsl nghttp2 python3 py3-impacket + # https://www.openbsd.org/faq/faq15.html + sudo pkg_add cmake ninja brotli openldap-client-- libssh2 libidn2 libpsl nghttp2 python3 py3-impacket cmake -B bld -G Ninja \ -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON \ -DCURL_WERROR=ON \ @@ -97,15 +100,17 @@ jobs: -DCURL_USE_OPENSSL=ON \ -DCURL_BROTLI=ON \ || { cat bld/CMakeFiles/CMake*.yaml; false; } - cmake --build bld --config Debug --parallel 3 + 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::' + cmake --build bld --config Debug bld/src/curl --disable --version if [ '${{ matrix.arch }}' = 'x86_64' ]; then # Slow on emulated CPU - cmake --build bld --config Debug --parallel 3 --target testdeps + cmake --build bld --config Debug --target testdeps export TFLAGS='-j8 ~3017 ~TFTP ~FTP' # FIXME: TFTP requests executed twice? Related: `curl: (69) TFTP: Access Violation`? cmake --build bld --config Debug --target test-ci fi echo '::group::build examples' - cmake --build bld --config Debug --parallel 3 --target curl-examples + cmake --build bld --config Debug --target curl-examples echo '::endgroup::' freebsd: @@ -120,7 +125,7 @@ jobs: - { build: 'cmake' , arch: 'arm64', compiler: 'clang' } fail-fast: false steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'autotools' if: ${{ matrix.build == 'autotools' }} uses: cross-platform-actions/action@cdc9ee69ef84a5f2e59c9058335d9c57bcb4ac86 # v0.25.0 @@ -131,7 +136,7 @@ jobs: run: | # https://ports.freebsd.org/ sudo pkg install -y autoconf automake libtool \ - pkgconf brotli openldap26-client libidn2 libnghttp2 nghttp2 stunnel py311-impacket + pkgconf brotli openldap26-client libidn2 libnghttp2 stunnel py311-impacket autoreconf -fi export CC='${{ matrix.compiler }}' mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-debug --enable-warnings --enable-werror \ @@ -139,6 +144,8 @@ jobs: --with-openssl \ --with-brotli --enable-ldap --enable-ldaps --with-libidn2 --with-libssh2 --with-nghttp2 --with-gssapi \ --disable-dependency-tracking || { tail -n 1000 config.log; false; } + echo '::group::curl_config.h (raw)'; cat lib/curl_config.h || true; echo '::endgroup::' + echo '::group::curl_config.h'; grep -F '#define' lib/curl_config.h | sort || true; echo '::endgroup::' make -j3 install src/curl --disable --version if [ '${{ matrix.arch }}' = 'x86_64' ]; then # Slow on emulated CPU @@ -158,8 +165,8 @@ jobs: architecture: ${{ matrix.arch }} run: | # https://ports.freebsd.org/ - sudo pkg install -y cmake ninja perl5 \ - pkgconf brotli openldap26-client libidn2 libnghttp2 nghttp2 stunnel py311-impacket + sudo pkg install -y cmake-core ninja perl5 \ + pkgconf brotli openldap26-client libidn2 libnghttp2 stunnel py311-impacket cmake -B bld -G Ninja \ '-DCMAKE_C_COMPILER=${{ matrix.compiler }}' \ -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON \ @@ -168,14 +175,16 @@ jobs: -DCURL_USE_OPENSSL=ON \ -DCURL_BROTLI=ON -DCURL_USE_GSSAPI=ON \ || { cat bld/CMakeFiles/CMake*.yaml; false; } - cmake --build bld --config Debug --parallel 3 + 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::' + cmake --build bld --config Debug bld/src/curl --disable --version if [ '${{ matrix.arch }}' = 'x86_64' ]; then # Slow on emulated CPU - cmake --build bld --config Debug --parallel 3 --target testdeps + cmake --build bld --config Debug --target testdeps cmake --build bld --config Debug --target test-ci fi echo '::group::build examples' - cmake --build bld --config Debug --parallel 3 --target curl-examples + cmake --build bld --config Debug --target curl-examples echo '::endgroup::' omnios: @@ -183,20 +192,23 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'autotools' - uses: vmactions/omnios-vm@bc3c64398d10bd00ecd8b3ca72db91c5a03dea77 # v1 + uses: vmactions/omnios-vm@16b5996777bc675acd3d537f13df536a526cd16d # v1 with: usesh: true - # https://pkg.omnios.org/r151050/core/en/index.shtml - prepare: pkg install build-essential libtool + # https://pkg.omnios.org/r151052/core/en/index.shtml + prepare: pkg install build-essential libtool nghttp2 run: | + set -e ln -s /usr/bin/gcpp /usr/bin/cpp # Some tests expect `cpp`, which is named `gcpp` in this env. autoreconf -fi mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-debug --enable-warnings --enable-werror \ --prefix="${HOME}"/install \ --with-openssl \ --disable-dependency-tracking || { tail -n 1000 config.log; false; } + echo '::group::curl_config.h (raw)'; cat lib/curl_config.h || true; echo '::endgroup::' + echo '::group::curl_config.h'; grep -F '#define' lib/curl_config.h | sort || true; echo '::endgroup::' gmake -j3 install src/curl --disable --version gmake -j3 -C tests diff --git a/.github/workflows/torture.yml b/.github/workflows/torture.yml deleted file mode 100644 index 6a5b030364d234..00000000000000 --- a/.github/workflows/torture.yml +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) Daniel Stenberg, , et al. -# -# SPDX-License-Identifier: curl - -name: Linux torture - -'on': - push: - branches: - - master - - '*/ci' - paths-ignore: - - '**/*.md' - - '**/CMakeLists.txt' - - '.circleci/**' - - 'appveyor.*' - - 'CMake/**' - - 'packages/**' - - 'plan9/**' - - 'projects/**' - - 'winbuild/**' - pull_request: - branches: - - master - paths-ignore: - - '**/*.md' - - '**/CMakeLists.txt' - - '.circleci/**' - - 'appveyor.*' - - 'CMake/**' - - 'packages/**' - - 'plan9/**' - - 'projects/**' - - 'winbuild/**' - -concurrency: - # Hardcoded workflow filename as workflow name above is just Linux again - group: torture-${{ github.event.pull_request.number || github.sha }} - cancel-in-progress: true - -permissions: {} - -jobs: - cmake: - name: '${{ matrix.build.name }}' - runs-on: 'ubuntu-24.04' - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - build: - - name: 'OpenSSL torture !FTP' - install: libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libnghttp2-dev libssh2-1-dev libc-ares-dev - generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON - tflags: -t --shallow=25 !FTP - - name: 'OpenSSL torture FTP' - install: libpsl-dev libbrotli-dev libzstd-dev zlib1g-dev libnghttp2-dev libssh2-1-dev libc-ares-dev - generate: -DCURL_USE_OPENSSL=ON -DENABLE_DEBUG=ON -DENABLE_ARES=ON - tflags: -t --shallow=20 FTP - - steps: - - run: | - sudo apt-get install cmake ninja-build pkgconf stunnel4 ${{ matrix.build.install }} - python3 -m pip install --break-system-packages impacket - name: 'install prereqs' - - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 - - - run: | - cmake -G Ninja -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \ - -DCURL_BROTLI=ON -DCURL_ZSTD=ON \ - ${{ matrix.build.generate }} - name: 'cmake configure' - - - run: cmake --build . --verbose - name: 'cmake build' - - - run: ./src/curl -V - name: 'check curl -V output' - - - run: cmake --build . --verbose --target testdeps - name: 'build tests' - - - run: cmake --build . --verbose --target test-torture - name: 'run tests' - env: - TFLAGS: '-j10 ${{ matrix.build.tflags }}' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 3479460f617dee..f484216a348c22 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,7 +54,7 @@ jobs: steps: - run: git config --global core.autocrlf input shell: pwsh - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: cygwin/cygwin-install-action@006ad0b0946ca6d0a3ea2d4437677fa767392401 # v4 with: platform: ${{ matrix.platform }} @@ -94,11 +94,9 @@ jobs: - name: 'curl_config.h' if: ${{ matrix.build == 'automake' }} - run: cat bld/lib/curl_config.h | grep -F '#define' | sort || true - - - name: 'curl_config.h (full)' - if: ${{ matrix.build == 'automake' }} - run: cat bld/lib/curl_config.h || true + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true - name: 'autotools build' if: ${{ matrix.build == 'automake' }} @@ -144,16 +142,14 @@ jobs: - name: 'curl_config.h' if: ${{ matrix.build == 'cmake' }} - run: cat bld/lib/curl_config.h | grep -F '#define' | sort || true - - - name: 'curl_config.h (full)' - if: ${{ matrix.build == 'cmake' }} - run: cat bld/lib/curl_config.h || true + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true - name: 'cmake build' if: ${{ matrix.build == 'cmake' }} timeout-minutes: 10 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 + run: cmake --build bld --config '${{ matrix.type }}' - name: 'curl version' if: ${{ matrix.build == 'cmake' }} @@ -166,7 +162,7 @@ jobs: - name: 'cmake build tests' if: ${{ matrix.build == 'cmake' && matrix.tflags != 'skipall' }} timeout-minutes: 15 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target testdeps + run: cmake --build bld --config '${{ matrix.type }}' --target testdeps - name: 'cmake run tests' if: ${{ matrix.build == 'cmake' && matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} @@ -182,7 +178,7 @@ jobs: - name: 'cmake build examples' if: ${{ matrix.build == 'cmake' }} timeout-minutes: 5 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target curl-examples + run: cmake --build bld --config '${{ matrix.type }}' --target curl-examples msys2: # both msys and mingw-w64 name: "${{ matrix.sys == 'msys' && 'msys2' || 'mingw' }}, ${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.env }} ${{ matrix.name }} ${{ matrix.test }}" @@ -205,13 +201,14 @@ jobs: - { build: 'cmake' , sys: 'ucrt64' , env: 'ucrt-x86_64' , tflags: 'skiprun' , config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DENABLE_CURLDEBUG=ON', type: 'Release', name: 'schannel R TrackMemory' } - { 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', type: 'Release', name: 'openssl' } - { build: 'cmake' , sys: 'mingw64', env: 'x86_64' , tflags: 'skiprun' , config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON', type: 'Release', test: 'uwp', name: 'schannel R' } + - { build: 'cmake' , sys: 'mingw64', env: 'x86_64' , tflags: 'skiprun' , config: '-DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON -DCMAKE_VERBOSE_MAKEFILE=ON', type: 'Debug', cflags: '-DCURL_SCHANNEL_DEV_DEBUG', name: 'schannel dev debug' } - { build: 'cmake' , sys: 'mingw32', env: 'i686' , tflags: 'skiprun' , config: '-DENABLE_DEBUG=OFF -DBUILD_SHARED_LIBS=ON -DCURL_USE_SCHANNEL=ON -DENABLE_UNICODE=ON', type: 'Release', name: 'schannel R' } fail-fast: false steps: - run: git config --global core.autocrlf input shell: pwsh - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - uses: msys2/setup-msys2@ddf331adaebd714795f1042345e6ca57bd66cea8 # v2 if: ${{ matrix.sys == 'msys' }} @@ -264,11 +261,9 @@ jobs: - name: 'curl_config.h' if: ${{ matrix.build == 'autotools' }} - run: cat bld/lib/curl_config.h | grep -F '#define' | sort || true - - - name: 'curl_config.h (full)' - if: ${{ matrix.build == 'autotools' }} - run: cat bld/lib/curl_config.h || true + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true - name: 'autotools build' if: ${{ matrix.build == 'autotools' }} @@ -279,6 +274,9 @@ jobs: if: ${{ matrix.build == 'autotools' }} timeout-minutes: 1 run: | + # avoid libtool's curl.exe wrapper + mv bld/src/.libs/curl.exe bld/src/curl.exe + mv bld/lib/.libs/*.dll bld/src || true find . -name '*.exe' -o -name '*.dll' bld/src/curl.exe --disable --version @@ -287,7 +285,7 @@ jobs: timeout-minutes: 10 run: make -C bld -j5 V=1 -C tests - - name: 'install test tools' + - name: 'install test prereqs' if: ${{ matrix.build == 'autotools' && matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} timeout-minutes: 5 run: | @@ -340,7 +338,7 @@ jobs: [ '${{ matrix.type }}' = 'Debug' ] && options+=' -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG=' [ '${{ matrix.type }}' = 'Release' ] && options+=' -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE=' cmake -B bld -G Ninja ${options} \ - "-DCMAKE_C_FLAGS=${cflags}" \ + "-DCMAKE_C_FLAGS=${{ matrix.cflags }} ${cflags}" \ "-DCMAKE_RC_COMPILE_OBJECT=${rcopts}" \ '-DCMAKE_BUILD_TYPE=${{ matrix.type }}' \ -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON \ @@ -354,16 +352,14 @@ jobs: - name: 'curl_config.h' if: ${{ matrix.build == 'cmake' }} - run: cat bld/lib/curl_config.h | grep -F '#define' | sort || true - - - name: 'curl_config.h (full)' - if: ${{ matrix.build == 'cmake' }} - run: cat bld/lib/curl_config.h || true + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true - name: 'cmake build' if: ${{ matrix.build == 'cmake' }} timeout-minutes: 10 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 + run: cmake --build bld --config '${{ matrix.type }}' - name: 'curl version' if: ${{ matrix.build == 'cmake' }} @@ -378,9 +374,9 @@ jobs: - name: 'cmake build tests' if: ${{ matrix.build == 'cmake' && matrix.tflags != 'skipall' }} timeout-minutes: 10 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target testdeps + run: cmake --build bld --config '${{ matrix.type }}' --target testdeps - - name: 'install test tools' + - name: 'install test prereqs' if: ${{ matrix.build == 'cmake' && matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} timeout-minutes: 5 run: | @@ -407,7 +403,7 @@ jobs: - name: 'cmake build examples' if: ${{ matrix.build == 'cmake' }} timeout-minutes: 5 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target curl-examples + run: cmake --build bld --config '${{ matrix.type }}' --target curl-examples old-mingw-w64: name: 'old-mingw, CM ${{ matrix.env }} ${{ matrix.name }}' @@ -443,7 +439,7 @@ jobs: fail-fast: false steps: - name: 'cache compiler (gcc ${{ matrix.env }})' - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4 + uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4 id: cache-compiler with: path: ~\my-cache @@ -463,7 +459,7 @@ jobs: ls -l - run: git config --global core.autocrlf input - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'cmake configure' timeout-minutes: 5 @@ -484,10 +480,9 @@ jobs: run: cat bld/CMakeFiles/CMake*.yaml 2>/dev/null || true - name: 'curl_config.h' - run: cat bld/lib/curl_config.h | grep -F '#define' | sort || true - - - name: 'curl_config.h (full)' - run: cat bld/lib/curl_config.h || true + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true - name: 'cmake build' timeout-minutes: 5 @@ -509,7 +504,7 @@ jobs: PATH="$(cygpath "${USERPROFILE}")/my-cache/${{ matrix.dir }}/bin:/c/msys64/usr/bin:$PATH" cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target testdeps - - name: 'install test tools' + - name: 'install test prereqs' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} timeout-minutes: 5 run: | @@ -550,7 +545,7 @@ jobs: - name: 'install packages' run: sudo apt-get --quiet 2 --option Dpkg::Use-Pty=0 install mingw-w64 ${{ matrix.build == 'cmake' && 'ninja-build' || '' }} - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'autotools autoreconf' if: ${{ matrix.build == 'autotools' }} @@ -569,6 +564,12 @@ jobs: if: ${{ matrix.build == 'autotools' && !cancelled() }} run: cat bld/config.log 2>/dev/null || true + - name: 'curl_config.h' + if: ${{ matrix.build == 'autotools' }} + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'autotools build' if: ${{ matrix.build == 'autotools' }} run: make -C bld -j5 @@ -593,17 +594,23 @@ jobs: if: ${{ matrix.build == 'cmake' && !cancelled() }} run: cat bld/CMakeFiles/CMake*.yaml 2>/dev/null || true + - name: 'curl_config.h' + if: ${{ matrix.build == 'cmake' }} + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true + - name: 'cmake build' if: ${{ matrix.build == 'cmake' }} - run: cmake --build bld --parallel 5 + run: cmake --build bld - name: 'cmake build tests' if: ${{ matrix.build == 'cmake' }} - run: cmake --build bld --parallel 5 --target testdeps + run: cmake --build bld --target testdeps - name: 'cmake build examples' if: ${{ matrix.build == 'cmake' }} - run: cmake --build bld --parallel 5 --target curl-examples + run: cmake --build bld --target curl-examples msvc: name: 'msvc, CM ${{ matrix.arch }}-${{ matrix.plat }} ${{ matrix.name }}' @@ -611,7 +618,7 @@ jobs: timeout-minutes: 55 defaults: run: - shell: bash + shell: C:\msys64\usr\bin\bash.exe {0} env: VCPKG_BINARY_SOURCES: 'clear;x-gha,readwrite' VCPKG_DISABLE_METRICS: '1' @@ -619,18 +626,18 @@ jobs: matrix: include: - name: 'schannel MultiSSL U' - install: 'brotli zlib zstd libpsl nghttp2 libssh2[core,zlib] pkgconf gsasl openssl mbedtls shiftmedia-libgnutls' + install: 'brotli zlib zstd libpsl nghttp2 libssh2[core,zlib] pkgconf gsasl openssl mbedtls' arch: 'x64' plat: 'windows' type: 'Debug' tflags: '~1516 ~2301 ~2302 ~2303 ~2307' config: >- -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_USE_LIBSSH2=ON - -DCURL_USE_SCHANNEL=ON -DCURL_USE_OPENSSL=ON -DCURL_USE_MBEDTLS=ON -DCURL_USE_GNUTLS=ON -DCURL_DEFAULT_SSL_BACKEND=schannel + -DCURL_USE_SCHANNEL=ON -DCURL_USE_OPENSSL=ON -DCURL_USE_MBEDTLS=ON -DCURL_DEFAULT_SSL_BACKEND=schannel -DCURL_USE_GSASL=ON -DUSE_WIN32_IDN=ON -DENABLE_UNICODE=ON - name: 'openssl' - install: 'brotli zlib zstd libpsl nghttp2 nghttp3 openssl libssh2 pkgconf gsasl c-ares libuv' + install: 'brotli zlib zstd libpsl nghttp2 nghttp3 openssl libssh2 pkgconf gsasl c-ares libuv krb5' arch: 'x64' plat: 'windows' type: 'Debug' @@ -638,7 +645,7 @@ jobs: config: >- -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_USE_LIBSSH2=ON -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON -DUSE_OPENSSL_QUIC=ON - -DCURL_USE_GSASL=ON -DENABLE_ARES=ON -DCURL_USE_LIBUV=ON + -DCURL_USE_GSASL=ON -DENABLE_ARES=ON -DCURL_USE_LIBUV=ON -DCURL_USE_GSSAPI=ON - name: 'openssl' install: 'brotli zlib zstd nghttp2 nghttp3 openssl libssh2' @@ -652,17 +659,17 @@ jobs: -DCURL_USE_LIBPSL=OFF - name: 'libressl' - install: 'brotli zlib zstd libpsl nghttp2 libressl libssh2[core,zlib]' + install: 'brotli zlib zstd libpsl nghttp2 libressl libssh2[core,zlib] pkgconf ngtcp2[libressl] nghttp3' arch: 'x64' plat: 'windows' type: 'Debug' tflags: '~1516 ~2301 ~2302 ~2303 ~2307' config: >- -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_USE_LIBSSH2=ON - -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON + -DCURL_USE_SCHANNEL=OFF -DCURL_USE_OPENSSL=ON -DUSE_NGTCP2=ON -DCURL_CA_SEARCH_SAFE=ON - - name: 'boringssl-ECH' + - name: 'boringssl' install: 'brotli zlib zstd libpsl nghttp2 boringssl libssh2[core,zlib]' arch: 'x64' plat: 'windows' @@ -684,8 +691,8 @@ jobs: -DCURL_USE_SCHANNEL=OFF -DCURL_USE_WOLFSSL=ON -DUSE_NGTCP2=ON -DCURL_USE_GSASL=ON - - name: 'gnutls' - install: 'brotli zlib zstd libpsl nghttp2 shiftmedia-libgnutls libssh pkgconf gsasl ngtcp2[gnutls] nghttp3' + - name: 'mbedtls' + install: 'brotli zlib zstd libpsl nghttp2 mbedtls libssh pkgconf gsasl' arch: 'x64' plat: 'windows' type: 'Debug' @@ -696,7 +703,7 @@ jobs: # https://github.com/curl/curl-for-win/blob/3951808deb04df9489ee17430f236ed54436f81a/libssh.sh#L6-L8 config: >- -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON - -DCURL_USE_SCHANNEL=OFF -DCURL_USE_GNUTLS=ON -DUSE_NGTCP2=ON + -DCURL_USE_SCHANNEL=OFF -DCURL_USE_MBEDTLS=ON -DCURL_USE_GSASL=ON - name: 'msh3' @@ -712,7 +719,7 @@ jobs: fail-fast: false steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - name: 'vcpkg cache setup' uses: actions/github-script@v7 @@ -729,21 +736,12 @@ jobs: - name: 'vcpkg build' timeout-minutes: 35 - run: | - # Temporary workaround pending: https://github.com/microsoft/vcpkg-tool/pull/1501 - export SystemDrive="$SYSTEMDRIVE" - export SystemRoot="$SYSTEMROOT" - export windir="$WINDIR" - vcpkg x-set-installed ${{ matrix.install }} '--triplet=${{ matrix.arch }}-${{ matrix.plat }}' + run: vcpkg x-set-installed ${{ matrix.install }} '--triplet=${{ matrix.arch }}-${{ matrix.plat }}' - name: 'cmake configure' timeout-minutes: 5 run: | - if [[ '${{ matrix.install }}' = *'libressl'* ]]; then - # without this, CMake gets confused by the non-vcpkg OpenSSL - # installed on the runner and fails when linking. - options+=" -DOPENSSL_ROOT_DIR=$VCPKG_INSTALLATION_ROOT/installed/${{ matrix.arch }}-${{ matrix.plat }}" - fi + PATH="/c/msys64/usr/bin:$PATH" cmake -B bld ${options} \ "-DCMAKE_TOOLCHAIN_FILE=$VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" \ "-DVCPKG_INSTALLED_DIR=$VCPKG_INSTALLATION_ROOT/installed" \ @@ -763,14 +761,15 @@ jobs: run: cat bld/CMakeFiles/CMake*.yaml 2>/dev/null || true - name: 'curl_config.h' - run: cat bld/lib/curl_config.h | grep -F '#define' | sort || true - - - name: 'curl_config.h (full)' - run: cat bld/lib/curl_config.h || true + run: | + echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::' + grep -F '#define' bld/lib/curl_config.h | sort || true - name: 'cmake build' timeout-minutes: 5 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 + run: | + PATH="/c/msys64/usr/bin:$PATH" + cmake --build bld --config '${{ matrix.type }}' --parallel 5 - name: 'curl version' timeout-minutes: 1 @@ -784,9 +783,11 @@ jobs: - name: 'cmake build tests' if: ${{ matrix.tflags != 'skipall' }} timeout-minutes: 10 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target testdeps + run: | + PATH="/c/msys64/usr/bin:$PATH" + cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target testdeps - - name: 'install test tools' + - name: 'install test prereqs' if: ${{ matrix.tflags != 'skipall' && matrix.tflags != 'skiprun' }} timeout-minutes: 5 run: | @@ -809,8 +810,11 @@ jobs: TFLAGS+=' ~614' # 'SFTP pre-quote chmod' SFTP, pre-quote, directory fi PATH="$PWD/bld/lib:$PATH:/c/Program Files (x86)/stunnel/bin:/c/Program Files/OpenSSH-Win64" + PATH="/c/msys64/usr/bin:$PATH" cmake --build bld --config '${{ matrix.type }}' --target test-ci - name: 'cmake build examples' timeout-minutes: 5 - run: cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target curl-examples + run: | + PATH="/c/msys64/usr/bin:$PATH" + cmake --build bld --config '${{ matrix.type }}' --parallel 5 --target curl-examples diff --git a/.mailmap b/.mailmap index 9705bf088003ab..56a1e7c4e268a8 100644 --- a/.mailmap +++ b/.mailmap @@ -111,3 +111,4 @@ Michael Osipov Christian Weisgerber Moritz Buhl Aki Sakurai <75532970+AkiSakurai@users.noreply.github.com> +Sinkevich Artem diff --git a/CMake/CurlSymbolHiding.cmake b/CMake/CurlSymbolHiding.cmake index 00b7b3c106cb33..16ec3feccc7162 100644 --- a/CMake/CurlSymbolHiding.cmake +++ b/CMake/CurlSymbolHiding.cmake @@ -21,8 +21,6 @@ # SPDX-License-Identifier: curl # ########################################################################### -include(CheckCSourceCompiles) - option(CURL_HIDDEN_SYMBOLS "Hide libcurl internal symbols (=hide all symbols that are not officially external)" ON) mark_as_advanced(CURL_HIDDEN_SYMBOLS) @@ -33,48 +31,36 @@ if(WIN32 AND (ENABLE_DEBUG OR ENABLE_CURLDEBUG)) set(CURL_HIDDEN_SYMBOLS OFF) endif() -if(CURL_HIDDEN_SYMBOLS) - set(_supports_symbol_hiding FALSE) +set(CURL_HIDES_PRIVATE_SYMBOLS FALSE) +unset(CURL_EXTERN_SYMBOL) +unset(CURL_CFLAG_SYMBOLS_HIDE) +if(CURL_HIDDEN_SYMBOLS) if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT MSVC) - set(_supports_symbol_hiding TRUE) - set(_symbol_extern "__attribute__ ((__visibility__ (\"default\")))") - set(_cflag_symbols_hide "-fvisibility=hidden") + set(CURL_HIDES_PRIVATE_SYMBOLS TRUE) + set(CURL_EXTERN_SYMBOL "__attribute__((__visibility__(\"default\")))") + set(CURL_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") elseif(CMAKE_COMPILER_IS_GNUCC) if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.4) # Note: This is considered buggy prior to 4.0 but the autotools do not care, so let us ignore that fact - set(_supports_symbol_hiding TRUE) - set(_symbol_extern "__attribute__ ((__visibility__ (\"default\")))") - set(_cflag_symbols_hide "-fvisibility=hidden") + set(CURL_HIDES_PRIVATE_SYMBOLS TRUE) + set(CURL_EXTERN_SYMBOL "__attribute__((__visibility__(\"default\")))") + set(CURL_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") endif() elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.0) - set(_supports_symbol_hiding TRUE) - set(_symbol_extern "__global") - set(_cflag_symbols_hide "-xldscope=hidden") - elseif(CMAKE_C_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0) - # Note: This should probably just check for version 9.1.045 but I am not 100% sure - # so let us do it the same way autotools do. - set(_supports_symbol_hiding TRUE) - set(_symbol_extern "__attribute__ ((__visibility__ (\"default\")))") - set(_cflag_symbols_hide "-fvisibility=hidden") - check_c_source_compiles("#include - int main(void) { printf(\"icc fvisibility bug test\"); return 0; }" _no_bug) - if(NOT _no_bug) - set(_supports_symbol_hiding FALSE) - set(_symbol_extern "") - set(_cflag_symbols_hide "") - endif() + set(CURL_HIDES_PRIVATE_SYMBOLS TRUE) + set(CURL_EXTERN_SYMBOL "__global") + set(CURL_CFLAG_SYMBOLS_HIDE "-xldscope=hidden") + elseif(CMAKE_C_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0) # Requires 9.1.045 + set(CURL_HIDES_PRIVATE_SYMBOLS TRUE) + set(CURL_EXTERN_SYMBOL "__attribute__((__visibility__(\"default\")))") + set(CURL_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden") elseif(MSVC) - set(_supports_symbol_hiding TRUE) + set(CURL_HIDES_PRIVATE_SYMBOLS TRUE) endif() - - set(CURL_HIDES_PRIVATE_SYMBOLS ${_supports_symbol_hiding}) else() if(MSVC) + # Note: This option is prone to export non-curl extra symbols. set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) endif() - set(CURL_HIDES_PRIVATE_SYMBOLS FALSE) endif() - -set(CURL_CFLAG_SYMBOLS_HIDE ${_cflag_symbols_hide}) -set(CURL_EXTERN_SYMBOL ${_symbol_extern}) diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index 5797586541ce18..03346187d6b050 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -50,6 +50,7 @@ int main(void) int flags = 0; if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK)) return 1; + ; return 0; } #endif @@ -159,15 +160,12 @@ int main(void) { return 0; } int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; -int main(void) { ; return 0; } +int main(void) { return 0; } #endif #ifdef HAVE_IOCTLSOCKET /* includes start */ #ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif # include #endif int main(void) @@ -184,9 +182,6 @@ int main(void) #ifdef HAVE_IOCTLSOCKET_CAMEL /* includes start */ #ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif # include #endif int main(void) @@ -202,9 +197,6 @@ int main(void) #ifdef HAVE_IOCTLSOCKET_CAMEL_FIONBIO /* includes start */ #ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif # include #endif int main(void) @@ -221,9 +213,6 @@ int main(void) #ifdef HAVE_IOCTLSOCKET_FIONBIO /* includes start */ #ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif # include #endif int main(void) @@ -296,9 +285,6 @@ int main(void) #ifdef HAVE_SETSOCKOPT_SO_NONBLOCK /* includes start */ #ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif # include #endif /* includes start */ @@ -409,9 +395,6 @@ int main(void) #ifdef HAVE_WIN32_WINNT /* includes start */ #ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif # ifndef NOGDI # define NOGDI # endif diff --git a/CMake/FindBearSSL.cmake b/CMake/FindBearSSL.cmake index dba4f5e6089407..ff55be0f22e048 100644 --- a/CMake/FindBearSSL.cmake +++ b/CMake/FindBearSSL.cmake @@ -21,19 +21,18 @@ # SPDX-License-Identifier: curl # ########################################################################### -# Find the bearssl library +# Find the BearSSL library # # Input variables: # -# BEARSSL_INCLUDE_DIR The bearssl include directory -# BEARSSL_INCLUDE_DIRS The bearssl include directory (deprecated) -# BEARSSL_LIBRARY Path to bearssl library +# - `BEARSSL_INCLUDE_DIR`: The BearSSL include directory. +# - `BEARSSL_LIBRARY`: Path to `bearssl` library. # # Result variables: # -# BEARSSL_FOUND System has bearssl -# BEARSSL_INCLUDE_DIRS The bearssl include directories -# BEARSSL_LIBRARIES The bearssl library names +# - `BEARSSL_FOUND`: System has BearSSL. +# - `BEARSSL_INCLUDE_DIRS`: The BearSSL include directories. +# - `BEARSSL_LIBRARIES`: The BearSSL library names. if(DEFINED BEARSSL_INCLUDE_DIRS AND NOT DEFINED BEARSSL_INCLUDE_DIR) message(WARNING "BEARSSL_INCLUDE_DIRS is deprecated, use BEARSSL_INCLUDE_DIR instead.") diff --git a/CMake/FindBrotli.cmake b/CMake/FindBrotli.cmake index 1150d4cc72ac14..767abf074fbc90 100644 --- a/CMake/FindBrotli.cmake +++ b/CMake/FindBrotli.cmake @@ -25,16 +25,16 @@ # # Input variables: # -# BROTLI_INCLUDE_DIR The brotli include directory -# BROTLICOMMON_LIBRARY Path to brotlicommon library -# BROTLIDEC_LIBRARY Path to brotlidec library +# - `BROTLI_INCLUDE_DIR`: The brotli include directory. +# - `BROTLICOMMON_LIBRARY`: Path to `brotlicommon` library. +# - `BROTLIDEC_LIBRARY`: Path to `brotlidec` library. # # Result variables: # -# BROTLI_FOUND System has brotli -# BROTLI_INCLUDE_DIRS The brotli include directories -# BROTLI_LIBRARIES The brotli library names -# BROTLI_VERSION Version of brotli +# - `BROTLI_FOUND`: System has brotli. +# - `BROTLI_INCLUDE_DIRS`: The brotli include directories. +# - `BROTLI_LIBRARIES`: The brotli library names. +# - `BROTLI_VERSION`: Version of brotli. if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) diff --git a/CMake/FindCares.cmake b/CMake/FindCares.cmake index e7b821afc4802d..ac55be16ddde48 100644 --- a/CMake/FindCares.cmake +++ b/CMake/FindCares.cmake @@ -25,15 +25,15 @@ # # Input variables: # -# CARES_INCLUDE_DIR The c-ares include directory -# CARES_LIBRARY Path to c-ares library +# - `CARES_INCLUDE_DIR`: The c-ares include directory. +# - `CARES_LIBRARY`: Path to `cares` library. # # Result variables: # -# CARES_FOUND System has c-ares -# CARES_INCLUDE_DIRS The c-ares include directories -# CARES_LIBRARIES The c-ares library names -# CARES_VERSION Version of c-ares +# - `CARES_FOUND`: System has c-ares. +# - `CARES_INCLUDE_DIRS`: The c-ares include directories. +# - `CARES_LIBRARIES`: The c-ares library names. +# - `CARES_VERSION`: Version of c-ares. if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) @@ -55,12 +55,22 @@ find_library(CARES_LIBRARY NAMES ${CARES_NAMES} "cares" if(PC_CARES_VERSION) set(CARES_VERSION ${PC_CARES_VERSION}) elseif(CARES_INCLUDE_DIR AND EXISTS "${CARES_INCLUDE_DIR}/ares_version.h") - set(_version_regex "#[\t ]*define[\t ]+ARES_VERSION_STR[\t ]+\"([^\"]*)\"") - file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(CARES_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) + set(_version_regex1 "#[\t ]*define[\t ]+ARES_VERSION_MAJOR[\t ]+([0-9]+).*") + set(_version_regex2 "#[\t ]*define[\t ]+ARES_VERSION_MINOR[\t ]+([0-9]+).*") + set(_version_regex3 "#[\t ]*define[\t ]+ARES_VERSION_PATCH[\t ]+([0-9]+).*") + file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str1 REGEX "${_version_regex1}") + file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str2 REGEX "${_version_regex2}") + file(STRINGS "${CARES_INCLUDE_DIR}/ares_version.h" _version_str3 REGEX "${_version_regex3}") + string(REGEX REPLACE "${_version_regex1}" "\\1" _version_str1 "${_version_str1}") + string(REGEX REPLACE "${_version_regex2}" "\\1" _version_str2 "${_version_str2}") + string(REGEX REPLACE "${_version_regex3}" "\\1" _version_str3 "${_version_str3}") + set(CARES_VERSION "${_version_str1}.${_version_str2}.${_version_str3}") + unset(_version_regex1) + unset(_version_regex2) + unset(_version_regex3) + unset(_version_str1) + unset(_version_str2) + unset(_version_str3) endif() include(FindPackageHandleStandardArgs) diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index e84f8947e2db6f..94fdc5f5db2dfc 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -25,20 +25,21 @@ # # Input variables: # -# GSS_ROOT_DIR Set this variable to the root installation of GSS +# - `GSS_ROOT_DIR`: Set this variable to the root installation of GSS. (also supported as environment) # # Result variables: # -# GSS_FOUND System has the Heimdal library -# GSS_FLAVOUR "MIT" or "Heimdal" if anything found -# GSS_INCLUDE_DIRS The GSS include directories -# GSS_LIBRARIES The GSS library names -# GSS_LIBRARY_DIRS The GSS library directories -# GSS_LDFLAGS Required linker flags -# GSS_CFLAGS Required compiler flags -# GSS_VERSION This is set to version advertised by pkg-config or read from manifest. -# In case the library is found but no version info available it is set to "unknown" +# - `GSS_FOUND`: System has the Heimdal library. +# - `GSS_FLAVOUR`: "GNU", "MIT" or "Heimdal" if anything found. +# - `GSS_INCLUDE_DIRS`: The GSS include directories. +# - `GSS_LIBRARIES`: The GSS library names. +# - `GSS_LIBRARY_DIRS`: The GSS library directories. +# - `GSS_PC_REQUIRES`: The GSS pkg-config packages. +# - `GSS_CFLAGS`: Required compiler flags. +# - `GSS_VERSION`: This is set to version advertised by pkg-config or read from manifest. +# In case the library is found but no version info available it is set to "unknown" +set(_gnu_modname "gss") set(_mit_modname "mit-krb5-gssapi") set(_heimdal_modname "heimdal-gssapi") @@ -55,7 +56,7 @@ set(_gss_root_hints if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}") if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) - pkg_search_module(_GSS ${_mit_modname} ${_heimdal_modname}) + pkg_search_module(_GSS ${_gnu_modname} ${_mit_modname} ${_heimdal_modname}) list(APPEND _gss_root_hints "${_GSS_PREFIX}") endif() if(WIN32) @@ -90,7 +91,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 ) - message(STATUS "FindGSS CFLAGS: ${_GSS_CFLAGS}") + message(STATUS "FindGSS krb5-config --cflags: ${_GSS_CFLAGS}") if(NOT _gss_configure_failed) # 0 means success # Should also work in an odd case when multiple directories are given string(STRIP "${_GSS_CFLAGS}" _GSS_CFLAGS) @@ -113,7 +114,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 ) - message(STATUS "FindGSS LDFLAGS: ${_gss_lib_flags}") + message(STATUS "FindGSS krb5-config --libs: ${_gss_lib_flags}") if(NOT _gss_configure_failed) # 0 means success # This script gives us libraries and link directories. Blah. We have to deal with it. @@ -128,8 +129,6 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr elseif(_flag MATCHES "^-L.*") string(REGEX REPLACE "^-L" "" _val "${_flag}") list(APPEND _GSS_LIBRARY_DIRS "${_val}") - else() - list(APPEND _GSS_LDFLAGS "${_flag}") endif() endforeach() endif() @@ -175,6 +174,7 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr ) if(_GSS_INCLUDE_DIRS) # jay, we have found something + cmake_push_check_state() set(CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIRS}") check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers) @@ -189,8 +189,8 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr if(_gss_have_roken_h OR _gss_have_heimdal_roken_h) set(GSS_FLAVOUR "Heimdal") endif() - list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS "-D__ROKEN_H__") endif() + cmake_pop_check_state() else() # I am not convinced if this is the right way but this is what autotools do at the moment find_path(_GSS_INCLUDE_DIRS NAMES "gssapi.h" @@ -203,6 +203,18 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr if(_GSS_INCLUDE_DIRS) set(GSS_FLAVOUR "Heimdal") + else() + find_path(_GSS_INCLUDE_DIRS NAMES "gss.h" + HINTS + ${_gss_root_hints} + PATH_SUFFIXES + "include" + ) + + if(_GSS_INCLUDE_DIRS) + set(GSS_FLAVOUR "GNU") + set(GSS_PC_REQUIRES "gss") + endif() endif() endif() @@ -216,14 +228,18 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr if(WIN32) if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND _gss_libdir_suffixes "lib/AMD64") - if(GSS_FLAVOUR STREQUAL "MIT") + if(GSS_FLAVOUR STREQUAL "GNU") + set(_gss_libname "gss") + elseif(GSS_FLAVOUR STREQUAL "MIT") set(_gss_libname "gssapi64") else() set(_gss_libname "libgssapi") endif() else() list(APPEND _gss_libdir_suffixes "lib/i386") - if(GSS_FLAVOUR STREQUAL "MIT") + if(GSS_FLAVOUR STREQUAL "GNU") + set(_gss_libname "gss") + elseif(GSS_FLAVOUR STREQUAL "MIT") set(_gss_libname "gssapi32") else() set(_gss_libname "libgssapi") @@ -231,7 +247,9 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr endif() else() list(APPEND _gss_libdir_suffixes "lib;lib64") # those suffixes are not checked for HINTS - if(GSS_FLAVOUR STREQUAL "MIT") + if(GSS_FLAVOUR STREQUAL "GNU") + set(_gss_libname "gss") + elseif(GSS_FLAVOUR STREQUAL "MIT") set(_gss_libname "gssapi_krb5") else() set(_gss_libname "gssapi") @@ -247,13 +265,22 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr endif() endif() else() - if(_GSS_MODULE_NAME STREQUAL _mit_modname OR _GSS_${_mit_modname}_VERSION) # _GSS_MODULE_NAME set since CMake 3.16 + # _GSS_MODULE_NAME set since CMake 3.16 + if(_GSS_MODULE_NAME STREQUAL _gnu_modname OR _GSS_${_gnu_modname}_VERSION) + set(GSS_FLAVOUR "GNU") + set(GSS_PC_REQUIRES "gss") + if(NOT _GSS_VERSION) # for old CMake versions? + set(_GSS_VERSION ${_GSS_${_gnu_modname}_VERSION}) + endif() + elseif(_GSS_MODULE_NAME STREQUAL _mit_modname OR _GSS_${_mit_modname}_VERSION) set(GSS_FLAVOUR "MIT") + set(GSS_PC_REQUIRES "mit-krb5-gssapi") if(NOT _GSS_VERSION) # for old CMake versions? set(_GSS_VERSION ${_GSS_${_mit_modname}_VERSION}) endif() else() set(GSS_FLAVOUR "Heimdal") + set(GSS_PC_REQUIRES "heimdal-gssapi") if(NOT _GSS_VERSION) # for old CMake versions? set(_GSS_VERSION ${_GSS_${_heimdal_modname}_VERSION}) endif() @@ -261,10 +288,11 @@ else() message(STATUS "Found GSS/${GSS_FLAVOUR} (via pkg-config): ${_GSS_INCLUDE_DIRS} (found version \"${_GSS_VERSION}\")") endif() +string(REPLACE ";" " " _GSS_CFLAGS "${_GSS_CFLAGS}") + set(GSS_INCLUDE_DIRS ${_GSS_INCLUDE_DIRS}) set(GSS_LIBRARIES ${_GSS_LIBRARIES}) set(GSS_LIBRARY_DIRS ${_GSS_LIBRARY_DIRS}) -set(GSS_LDFLAGS ${_GSS_LDFLAGS}) set(GSS_CFLAGS ${_GSS_CFLAGS}) set(GSS_VERSION ${_GSS_VERSION}) @@ -294,6 +322,15 @@ if(GSS_FLAVOUR) else() set(GSS_VERSION "MIT Unknown") endif() + elseif(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "GNU") + if(GSS_INCLUDE_DIRS AND EXISTS "${GSS_INCLUDE_DIRS}/gss.h") + set(_version_regex "#[\t ]*define[\t ]+GSS_VERSION[\t ]+\"([^\"]*)\"") + file(STRINGS "${GSS_INCLUDE_DIRS}/gss.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(GSS_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() endif() endif() @@ -312,7 +349,6 @@ mark_as_advanced( _GSS_CFLAGS _GSS_FOUND _GSS_INCLUDE_DIRS - _GSS_LDFLAGS _GSS_LIBRARIES _GSS_LIBRARY_DIRS _GSS_MODULE_NAME diff --git a/CMake/FindLibgsasl.cmake b/CMake/FindLibgsasl.cmake index ed930ebb766213..82ed07edea99f2 100644 --- a/CMake/FindLibgsasl.cmake +++ b/CMake/FindLibgsasl.cmake @@ -25,17 +25,17 @@ # # Input variables: # -# LIBGSASL_INCLUDE_DIR The libgsasl include directory -# LIBGSASL_LIBRARY Path to libgsasl library +# - `LIBGSASL_INCLUDE_DIR`: The libgsasl include directory. +# - `LIBGSASL_LIBRARY`: Path to `libgsasl` library. # # Result variables: # -# LIBGSASL_FOUND System has libgsasl -# LIBGSASL_INCLUDE_DIRS The libgsasl include directories -# LIBGSASL_LIBRARIES The libgsasl library names -# LIBGSASL_LIBRARY_DIRS The libgsasl library directories -# LIBGSASL_CFLAGS Required compiler flags -# LIBGSASL_VERSION Version of libgsasl +# - `LIBGSASL_FOUND`: System has libgsasl. +# - `LIBGSASL_INCLUDE_DIRS`: The libgsasl include directories. +# - `LIBGSASL_LIBRARIES`: The libgsasl library names. +# - `LIBGSASL_LIBRARY_DIRS`: The libgsasl library directories. +# - `LIBGSASL_CFLAGS`: Required compiler flags. +# - `LIBGSASL_VERSION`: Version of libgsasl. if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBGSASL_INCLUDE_DIR AND @@ -51,6 +51,7 @@ else() find_path(LIBGSASL_INCLUDE_DIR NAMES "gsasl.h") find_library(LIBGSASL_LIBRARY NAMES "gsasl" "libgsasl") + unset(LIBGSASL_VERSION CACHE) if(LIBGSASL_INCLUDE_DIR AND EXISTS "${LIBGSASL_INCLUDE_DIR}/gsasl-version.h") set(_version_regex "#[\t ]*define[\t ]+GSASL_VERSION[\t ]+\"([^\"]*)\"") file(STRINGS "${LIBGSASL_INCLUDE_DIR}/gsasl-version.h" _version_str REGEX "${_version_regex}") diff --git a/CMake/FindLibidn2.cmake b/CMake/FindLibidn2.cmake index 47d4a58623e37f..35580ae28be54e 100644 --- a/CMake/FindLibidn2.cmake +++ b/CMake/FindLibidn2.cmake @@ -25,17 +25,17 @@ # # Input variables: # -# LIBIDN2_INCLUDE_DIR The libidn2 include directory -# LIBIDN2_LIBRARY Path to libidn2 library +# - `LIBIDN2_INCLUDE_DIR`: The libidn2 include directory. +# - `LIBIDN2_LIBRARY`: Path to `libidn2` library. # # Result variables: # -# LIBIDN2_FOUND System has libidn2 -# LIBIDN2_INCLUDE_DIRS The libidn2 include directories -# LIBIDN2_LIBRARIES The libidn2 library names -# LIBIDN2_LIBRARY_DIRS The libidn2 library directories -# LIBIDN2_CFLAGS Required compiler flags -# LIBIDN2_VERSION Version of libidn2 +# - `LIBIDN2_FOUND`: System has libidn2. +# - `LIBIDN2_INCLUDE_DIRS`: The libidn2 include directories. +# - `LIBIDN2_LIBRARIES`: The libidn2 library names. +# - `LIBIDN2_LIBRARY_DIRS`: The libidn2 library directories. +# - `LIBIDN2_CFLAGS`: Required compiler flags. +# - `LIBIDN2_VERSION`: Version of libidn2. if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBIDN2_INCLUDE_DIR AND @@ -51,6 +51,7 @@ else() find_path(LIBIDN2_INCLUDE_DIR NAMES "idn2.h") find_library(LIBIDN2_LIBRARY NAMES "idn2" "libidn2") + unset(LIBIDN2_VERSION CACHE) if(LIBIDN2_INCLUDE_DIR AND EXISTS "${LIBIDN2_INCLUDE_DIR}/idn2.h") set(_version_regex "#[\t ]*define[\t ]+IDN2_VERSION[\t ]+\"([^\"]*)\"") file(STRINGS "${LIBIDN2_INCLUDE_DIR}/idn2.h" _version_str REGEX "${_version_regex}") diff --git a/CMake/FindLibpsl.cmake b/CMake/FindLibpsl.cmake index b2cd64f0db02f9..bb323f4b8cc505 100644 --- a/CMake/FindLibpsl.cmake +++ b/CMake/FindLibpsl.cmake @@ -25,15 +25,15 @@ # # Input variables: # -# LIBPSL_INCLUDE_DIR The libpsl include directory -# LIBPSL_LIBRARY Path to libpsl library +# - `LIBPSL_INCLUDE_DIR`: The libpsl include directory. +# - `LIBPSL_LIBRARY`: Path to `libpsl` library. # # Result variables: # -# LIBPSL_FOUND System has libpsl -# LIBPSL_INCLUDE_DIRS The libpsl include directories -# LIBPSL_LIBRARIES The libpsl library names -# LIBPSL_VERSION Version of libpsl +# - `LIBPSL_FOUND`: System has libpsl. +# - `LIBPSL_INCLUDE_DIRS`: The libpsl include directories. +# - `LIBPSL_LIBRARIES`: The libpsl library names. +# - `LIBPSL_VERSION`: Version of libpsl. if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) diff --git a/CMake/FindLibssh.cmake b/CMake/FindLibssh.cmake index 7dc019be7564fc..2b95fd80a3b2c4 100644 --- a/CMake/FindLibssh.cmake +++ b/CMake/FindLibssh.cmake @@ -25,17 +25,17 @@ # # Input variables: # -# LIBSSH_INCLUDE_DIR The libssh include directory -# LIBSSH_LIBRARY Path to libssh library +# LIBSSH_INCLUDE_DIR The libssh include directory. +# LIBSSH_LIBRARY Path to libssh library. # # Result variables: # -# LIBSSH_FOUND System has libssh -# LIBSSH_INCLUDE_DIRS The libssh include directories -# LIBSSH_LIBRARIES The libssh library names -# LIBSSH_LIBRARY_DIRS The libssh library directories -# LIBSSH_CFLAGS Required compiler flags -# LIBSSH_VERSION Version of libssh +# LIBSSH_FOUND System has libssh. +# LIBSSH_INCLUDE_DIRS The libssh include directories. +# LIBSSH_LIBRARIES The libssh library names. +# LIBSSH_LIBRARY_DIRS The libssh library directories. +# LIBSSH_CFLAGS Required compiler flags. +# LIBSSH_VERSION Version of libssh. if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBSSH_INCLUDE_DIR AND @@ -51,6 +51,7 @@ else() find_path(LIBSSH_INCLUDE_DIR NAMES "libssh/libssh.h") find_library(LIBSSH_LIBRARY NAMES "ssh" "libssh") + unset(LIBSSH_VERSION CACHE) if(LIBSSH_INCLUDE_DIR AND EXISTS "${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h") set(_version_regex1 "#[\t ]*define[\t ]+LIBSSH_VERSION_MAJOR[\t ]+([0-9]+).*") set(_version_regex2 "#[\t ]*define[\t ]+LIBSSH_VERSION_MINOR[\t ]+([0-9]+).*") diff --git a/CMake/FindLibssh2.cmake b/CMake/FindLibssh2.cmake index 0007a8a7267af6..0785214ad9b0b0 100644 --- a/CMake/FindLibssh2.cmake +++ b/CMake/FindLibssh2.cmake @@ -25,15 +25,15 @@ # # Input variables: # -# LIBSSH2_INCLUDE_DIR The libssh2 include directory -# LIBSSH2_LIBRARY Path to libssh2 library +# - `LIBSSH2_INCLUDE_DIR`: The libssh2 include directory. +# - `LIBSSH2_LIBRARY`: Path to `libssh2` library. # # Result variables: # -# LIBSSH2_FOUND System has libssh2 -# LIBSSH2_INCLUDE_DIRS The libssh2 include directories -# LIBSSH2_LIBRARIES The libssh2 library names -# LIBSSH2_VERSION Version of libssh2 +# - `LIBSSH2_FOUND`: System has libssh2. +# - `LIBSSH2_INCLUDE_DIRS`: The libssh2 include directories. +# - `LIBSSH2_LIBRARIES`: The libssh2 library names. +# - `LIBSSH2_VERSION`: Version of libssh2. if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) diff --git a/CMake/FindLibuv.cmake b/CMake/FindLibuv.cmake index d4dfa2450332d8..d647e3438f6943 100644 --- a/CMake/FindLibuv.cmake +++ b/CMake/FindLibuv.cmake @@ -25,17 +25,17 @@ # # Input variables: # -# LIBUV_INCLUDE_DIR The libuv include directory -# LIBUV_LIBRARY Path to libuv library +# - `LIBUV_INCLUDE_DIR`: The libuv include directory. +# - `LIBUV_LIBRARY`: Path to `libuv` library. # # Result variables: # -# LIBUV_FOUND System has libuv -# LIBUV_INCLUDE_DIRS The libuv include directories -# LIBUV_LIBRARIES The libuv library names -# LIBUV_LIBRARY_DIRS The libuv library directories -# LIBUV_CFLAGS Required compiler flags -# LIBUV_VERSION Version of libuv +# - `LIBUV_FOUND`: System has libuv. +# - `LIBUV_INCLUDE_DIRS`: The libuv include directories. +# - `LIBUV_LIBRARIES`: The libuv library names. +# - `LIBUV_LIBRARY_DIRS`: The libuv library directories. +# - `LIBUV_CFLAGS`: Required compiler flags. +# - `LIBUV_VERSION`: Version of libuv. if(CURL_USE_PKGCONFIG AND NOT DEFINED LIBUV_INCLUDE_DIR AND @@ -51,6 +51,7 @@ else() find_path(LIBUV_INCLUDE_DIR NAMES "uv.h") find_library(LIBUV_LIBRARY NAMES "uv" "libuv") + unset(LIBUV_VERSION CACHE) if(LIBUV_INCLUDE_DIR AND EXISTS "${LIBUV_INCLUDE_DIR}/uv/version.h") set(_version_regex1 "#[\t ]*define[\t ]+UV_VERSION_MAJOR[\t ]+([0-9]+).*") set(_version_regex2 "#[\t ]*define[\t ]+UV_VERSION_MINOR[\t ]+([0-9]+).*") diff --git a/CMake/FindMSH3.cmake b/CMake/FindMSH3.cmake index 46cee88719a0f4..387d30b24e018c 100644 --- a/CMake/FindMSH3.cmake +++ b/CMake/FindMSH3.cmake @@ -25,49 +25,45 @@ # # Input variables: # -# MSH3_INCLUDE_DIR The msh3 include directory -# MSH3_LIBRARY Path to msh3 library +# - `MSH3_INCLUDE_DIR`: The msh3 include directory. +# - `MSH3_LIBRARY`: Path to `msh3` library. # # Result variables: # -# MSH3_FOUND System has msh3 -# MSH3_INCLUDE_DIRS The msh3 include directories -# MSH3_LIBRARIES The msh3 library names -# MSH3_VERSION Version of msh3 +# - `MSH3_FOUND`: System has msh3. +# - `MSH3_INCLUDE_DIRS`: The msh3 include directories. +# - `MSH3_LIBRARIES`: The msh3 library names. +# - `MSH3_LIBRARY_DIRS`: The msh3 library directories. +# - `MSH3_PC_REQUIRES`: The msh3 pkg-config packages. +# - `MSH3_CFLAGS`: Required compiler flags. +# - `MSH3_VERSION`: Version of msh3. -if(CURL_USE_PKGCONFIG) +if(CURL_USE_PKGCONFIG AND + NOT DEFINED MSH3_INCLUDE_DIR AND + NOT DEFINED MSH3_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_MSH3 "libmsh3") + pkg_check_modules(MSH3 "libmsh3") endif() -find_path(MSH3_INCLUDE_DIR NAMES "msh3.h" - HINTS - ${PC_MSH3_INCLUDEDIR} - ${PC_MSH3_INCLUDE_DIRS} -) - -find_library(MSH3_LIBRARY NAMES "msh3" - HINTS - ${PC_MSH3_LIBDIR} - ${PC_MSH3_LIBRARY_DIRS} -) +if(MSH3_FOUND) + set(MSH3_PC_REQUIRES "libmsh3") + string(REPLACE ";" " " MSH3_CFLAGS "${MSH3_CFLAGS}") + message(STATUS "Found MSH3 (via pkg-config): ${MSH3_INCLUDE_DIRS} (found version \"${MSH3_VERSION}\")") +else() + find_path(MSH3_INCLUDE_DIR NAMES "msh3.h") + find_library(MSH3_LIBRARY NAMES "msh3") -if(PC_MSH3_VERSION) - set(MSH3_VERSION ${PC_MSH3_VERSION}) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(MSH3 + REQUIRED_VARS + MSH3_INCLUDE_DIR + MSH3_LIBRARY + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(MSH3 - REQUIRED_VARS - MSH3_INCLUDE_DIR - MSH3_LIBRARY - VERSION_VAR - MSH3_VERSION -) + if(MSH3_FOUND) + set(MSH3_INCLUDE_DIRS ${MSH3_INCLUDE_DIR}) + set(MSH3_LIBRARIES ${MSH3_LIBRARY}) + endif() -if(MSH3_FOUND) - set(MSH3_INCLUDE_DIRS ${MSH3_INCLUDE_DIR}) - set(MSH3_LIBRARIES ${MSH3_LIBRARY}) + mark_as_advanced(MSH3_INCLUDE_DIR MSH3_LIBRARY) endif() - -mark_as_advanced(MSH3_INCLUDE_DIR MSH3_LIBRARY) diff --git a/CMake/FindMbedTLS.cmake b/CMake/FindMbedTLS.cmake index 53b86149e4ea41..e361c9636a8355 100644 --- a/CMake/FindMbedTLS.cmake +++ b/CMake/FindMbedTLS.cmake @@ -21,22 +21,24 @@ # SPDX-License-Identifier: curl # ########################################################################### -# Find the mbedtls library +# Find the mbedTLS library # # Input variables: # -# MBEDTLS_INCLUDE_DIR The mbedtls include directory -# MBEDTLS_INCLUDE_DIRS The mbedtls include directory (deprecated) -# MBEDTLS_LIBRARY Path to mbedtls library -# MBEDX509_LIBRARY Path to mbedx509 library -# MBEDCRYPTO_LIBRARY Path to mbedcrypto library +# - `MBEDTLS_INCLUDE_DIR`: The mbedTLS include directory. +# - `MBEDTLS_LIBRARY`: Path to `mbedtls` library. +# - `MBEDX509_LIBRARY`: Path to `mbedx509` library. +# - `MBEDCRYPTO_LIBRARY`: Path to `mbedcrypto` library. # # Result variables: # -# MBEDTLS_FOUND System has mbedtls -# MBEDTLS_INCLUDE_DIRS The mbedtls include directories -# MBEDTLS_LIBRARIES The mbedtls library names -# MBEDTLS_VERSION Version of mbedtls +# - `MBEDTLS_FOUND`: System has mbedTLS. +# - `MBEDTLS_INCLUDE_DIRS`: The mbedTLS include directories. +# - `MBEDTLS_LIBRARIES`: The mbedTLS library names. +# - `MBEDTLS_LIBRARY_DIRS`: The mbedTLS library directories. +# - `MBEDTLS_PC_REQUIRES`: The mbedTLS pkg-config packages. +# - `MBEDTLS_CFLAGS`: Required compiler flags. +# - `MBEDTLS_VERSION`: Version of mbedTLS. if(DEFINED MBEDTLS_INCLUDE_DIRS AND NOT DEFINED MBEDTLS_INCLUDE_DIR) message(WARNING "MBEDTLS_INCLUDE_DIRS is deprecated, use MBEDTLS_INCLUDE_DIR instead.") @@ -44,68 +46,64 @@ if(DEFINED MBEDTLS_INCLUDE_DIRS AND NOT DEFINED MBEDTLS_INCLUDE_DIR) unset(MBEDTLS_INCLUDE_DIRS) endif() -if(CURL_USE_PKGCONFIG) +if(CURL_USE_PKGCONFIG AND + NOT DEFINED MBEDTLS_INCLUDE_DIR AND + NOT DEFINED MBEDTLS_LIBRARY AND + NOT DEFINED MBEDX509_LIBRARY AND + NOT DEFINED MBEDCRYPTO_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_MBEDTLS "mbedtls") + pkg_check_modules(MBEDTLS "mbedtls") + pkg_check_modules(MBEDX509 "mbedx509") + pkg_check_modules(MBEDCRYPTO "mbedcrypto") endif() -find_path(MBEDTLS_INCLUDE_DIR NAMES "mbedtls/ssl.h" - HINTS - ${PC_MBEDTLS_INCLUDEDIR} - ${PC_MBEDTLS_INCLUDE_DIRS} -) +if(MBEDTLS_FOUND AND MBEDX509_FOUND AND MBEDCRYPTO_FOUND) + list(APPEND MBEDTLS_LIBRARIES ${MBEDX509_LIBRARIES} ${MBEDCRYPTO_LIBRARIES}) + list(REMOVE_DUPLICATES MBEDTLS_LIBRARIES) + set(MBEDTLS_PC_REQUIRES "mbedtls") + string(REPLACE ";" " " MBEDTLS_CFLAGS "${MBEDTLS_CFLAGS}") + message(STATUS "Found MbedTLS (via pkg-config): ${MBEDTLS_INCLUDE_DIRS} (found version \"${MBEDTLS_VERSION}\")") +else() + find_path(MBEDTLS_INCLUDE_DIR NAMES "mbedtls/ssl.h") + find_library(MBEDTLS_LIBRARY NAMES "mbedtls" "libmbedtls") + find_library(MBEDX509_LIBRARY NAMES "mbedx509" "libmbedx509") + find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" "libmbedcrypto") -find_library(MBEDTLS_LIBRARY NAMES "mbedtls" - HINTS - ${PC_MBEDTLS_LIBDIR} - ${PC_MBEDTLS_LIBRARY_DIRS} -) -find_library(MBEDX509_LIBRARY NAMES "mbedx509" - HINTS - ${PC_MBEDTLS_LIBDIR} - ${PC_MBEDTLS_LIBRARY_DIRS} -) -find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" - HINTS - ${PC_MBEDTLS_LIBDIR} - ${PC_MBEDTLS_LIBRARY_DIRS} -) - -if(PC_MBEDTLS_VERSION) - set(MBEDTLS_VERSION ${PC_MBEDTLS_VERSION}) -elseif(MBEDTLS_INCLUDE_DIR) - if(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") # 3.x - set(_version_header "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") - elseif(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h") # 2.x - set(_version_header "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h") - else() - unset(_version_header) - endif() - if(_version_header) - set(_version_regex "#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"([0-9.]+)\"") - file(STRINGS "${_version_header}" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(MBEDTLS_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) - unset(_version_header) + unset(MBEDTLS_VERSION CACHE) + if(MBEDTLS_INCLUDE_DIR) + if(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") # 3.x + set(_version_header "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") + elseif(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h") # 2.x + set(_version_header "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h") + else() + unset(_version_header) + endif() + if(_version_header) + set(_version_regex "#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"([0-9.]+)\"") + file(STRINGS "${_version_header}" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(MBEDTLS_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + unset(_version_header) + endif() endif() -endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(MbedTLS - REQUIRED_VARS - MBEDTLS_INCLUDE_DIR - MBEDTLS_LIBRARY - MBEDX509_LIBRARY - MBEDCRYPTO_LIBRARY - VERSION_VAR - MBEDTLS_VERSION -) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(MbedTLS + REQUIRED_VARS + MBEDTLS_INCLUDE_DIR + MBEDTLS_LIBRARY + MBEDX509_LIBRARY + MBEDCRYPTO_LIBRARY + VERSION_VAR + MBEDTLS_VERSION + ) -if(MBEDTLS_FOUND) - set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) - set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) -endif() + if(MBEDTLS_FOUND) + set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) + endif() -mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) + mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY) +endif() diff --git a/CMake/FindNGHTTP2.cmake b/CMake/FindNGHTTP2.cmake index e632d2054b0854..a0aacbf27be175 100644 --- a/CMake/FindNGHTTP2.cmake +++ b/CMake/FindNGHTTP2.cmake @@ -25,15 +25,15 @@ # # Input variables: # -# NGHTTP2_INCLUDE_DIR The nghttp2 include directory -# NGHTTP2_LIBRARY Path to nghttp2 library +# - `NGHTTP2_INCLUDE_DIR`: The nghttp2 include directory. +# - `NGHTTP2_LIBRARY`: Path to `nghttp2` library. # # Result variables: # -# NGHTTP2_FOUND System has nghttp2 -# NGHTTP2_INCLUDE_DIRS The nghttp2 include directories -# NGHTTP2_LIBRARIES The nghttp2 library names -# NGHTTP2_VERSION Version of nghttp2 +# - `NGHTTP2_FOUND`: System has nghttp2. +# - `NGHTTP2_INCLUDE_DIRS`: The nghttp2 include directories. +# - `NGHTTP2_LIBRARIES`: The nghttp2 library names. +# - `NGHTTP2_VERSION`: Version of nghttp2. if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) diff --git a/CMake/FindNGHTTP3.cmake b/CMake/FindNGHTTP3.cmake index 02a0c87d072563..1adbd6047b8a61 100644 --- a/CMake/FindNGHTTP3.cmake +++ b/CMake/FindNGHTTP3.cmake @@ -25,15 +25,15 @@ # # Input variables: # -# NGHTTP3_INCLUDE_DIR The nghttp3 include directory -# NGHTTP3_LIBRARY Path to nghttp3 library +# - `NGHTTP3_INCLUDE_DIR`: The nghttp3 include directory. +# - `NGHTTP3_LIBRARY`: Path to `nghttp3` library. # # Result variables: # -# NGHTTP3_FOUND System has nghttp3 -# NGHTTP3_INCLUDE_DIRS The nghttp3 include directories -# NGHTTP3_LIBRARIES The nghttp3 library names -# NGHTTP3_VERSION Version of nghttp3 +# - `NGHTTP3_FOUND`: System has nghttp3. +# - `NGHTTP3_INCLUDE_DIRS`: The nghttp3 include directories. +# - `NGHTTP3_LIBRARIES`: The nghttp3 library names. +# - `NGHTTP3_VERSION`: Version of nghttp3. if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) diff --git a/CMake/FindNGTCP2.cmake b/CMake/FindNGTCP2.cmake index c6d66bfb789c53..99b022dc41ece6 100644 --- a/CMake/FindNGTCP2.cmake +++ b/CMake/FindNGTCP2.cmake @@ -26,22 +26,22 @@ # This module accepts optional COMPONENTS to control the crypto library (these are # mutually exclusive): # -# quictls: Use libngtcp2_crypto_quictls (choose this for LibreSSL) -# BoringSSL: Use libngtcp2_crypto_boringssl (choose this for AWS-LC) -# wolfSSL: Use libngtcp2_crypto_wolfssl -# GnuTLS: Use libngtcp2_crypto_gnutls +# - quictls: Use `libngtcp2_crypto_quictls`. (choose this for LibreSSL) +# - BoringSSL: Use `libngtcp2_crypto_boringssl`. (choose this for AWS-LC) +# - wolfSSL: Use `libngtcp2_crypto_wolfssl`. +# - GnuTLS: Use `libngtcp2_crypto_gnutls`. # # Input variables: # -# NGTCP2_INCLUDE_DIR The ngtcp2 include directory -# NGTCP2_LIBRARY Path to ngtcp2 library +# - `NGTCP2_INCLUDE_DIR`: The ngtcp2 include directory. +# - `NGTCP2_LIBRARY`: Path to `ngtcp2` library. # # Result variables: # -# NGTCP2_FOUND System has ngtcp2 -# NGTCP2_INCLUDE_DIRS The ngtcp2 include directories -# NGTCP2_LIBRARIES The ngtcp2 library names -# NGTCP2_VERSION Version of ngtcp2 +# - `NGTCP2_FOUND`: System has ngtcp2. +# - `NGTCP2_INCLUDE_DIRS`: The ngtcp2 include directories. +# - `NGTCP2_LIBRARIES`: The ngtcp2 library names. +# - `NGTCP2_VERSION`: Version of ngtcp2. if(CURL_USE_PKGCONFIG) find_package(PkgConfig QUIET) diff --git a/CMake/FindNettle.cmake b/CMake/FindNettle.cmake index b5da05bf79a7fc..56f2a940b30400 100644 --- a/CMake/FindNettle.cmake +++ b/CMake/FindNettle.cmake @@ -25,17 +25,17 @@ # # Input variables: # -# NETTLE_INCLUDE_DIR The nettle include directory -# NETTLE_LIBRARY Path to nettle library +# - `NETTLE_INCLUDE_DIR`: The nettle include directory. +# - `NETTLE_LIBRARY`: Path to `nettle` library. # # Result variables: # -# NETTLE_FOUND System has nettle -# NETTLE_INCLUDE_DIRS The nettle include directories -# NETTLE_LIBRARIES The nettle library names -# NETTLE_LIBRARY_DIRS The nettle library directories -# NETTLE_CFLAGS Required compiler flags -# NETTLE_VERSION Version of nettle +# - `NETTLE_FOUND`: System has nettle. +# - `NETTLE_INCLUDE_DIRS`: The nettle include directories. +# - `NETTLE_LIBRARIES`: The nettle library names. +# - `NETTLE_LIBRARY_DIRS`: The nettle library directories. +# - `NETTLE_CFLAGS`: Required compiler flags. +# - `NETTLE_VERSION`: Version of nettle. if(CURL_USE_PKGCONFIG AND NOT DEFINED NETTLE_INCLUDE_DIR AND @@ -51,6 +51,7 @@ else() find_path(NETTLE_INCLUDE_DIR NAMES "nettle/sha2.h") find_library(NETTLE_LIBRARY NAMES "nettle") + unset(NETTLE_VERSION CACHE) if(NETTLE_INCLUDE_DIR AND EXISTS "${NETTLE_INCLUDE_DIR}/nettle/version.h") set(_version_regex1 "#[\t ]*define[ \t]+NETTLE_VERSION_MAJOR[ \t]+([0-9]+).*") set(_version_regex2 "#[\t ]*define[ \t]+NETTLE_VERSION_MINOR[ \t]+([0-9]+).*") diff --git a/CMake/FindQuiche.cmake b/CMake/FindQuiche.cmake index 7d1626adc6443a..6db5cb0c1c0731 100644 --- a/CMake/FindQuiche.cmake +++ b/CMake/FindQuiche.cmake @@ -25,49 +25,43 @@ # # Input variables: # -# QUICHE_INCLUDE_DIR The quiche include directory -# QUICHE_LIBRARY Path to quiche library +# - `QUICHE_INCLUDE_DIR`: The quiche include directory. +# - `QUICHE_LIBRARY`: Path to `quiche` library. # # Result variables: # -# QUICHE_FOUND System has quiche -# QUICHE_INCLUDE_DIRS The quiche include directories -# QUICHE_LIBRARIES The quiche library names -# QUICHE_VERSION Version of quiche +# - `QUICHE_FOUND`: System has quiche. +# - `QUICHE_INCLUDE_DIRS`: The quiche include directories. +# - `QUICHE_LIBRARIES`: The quiche library names. +# - `QUICHE_LIBRARY_DIRS`: The quiche library directories. +# - `QUICHE_CFLAGS`: Required compiler flags. +# - `QUICHE_VERSION`: Version of quiche. -if(CURL_USE_PKGCONFIG) +if(CURL_USE_PKGCONFIG AND + NOT DEFINED QUICHE_INCLUDE_DIR AND + NOT DEFINED QUICHE_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_QUICHE "quiche") + pkg_check_modules(QUICHE "quiche") endif() -find_path(QUICHE_INCLUDE_DIR NAMES "quiche.h" - HINTS - ${PC_QUICHE_INCLUDEDIR} - ${PC_QUICHE_INCLUDE_DIRS} -) - -find_library(QUICHE_LIBRARY NAMES "quiche" - HINTS - ${PC_QUICHE_LIBDIR} - ${PC_QUICHE_LIBRARY_DIRS} -) +if(QUICHE_FOUND) + string(REPLACE ";" " " QUICHE_CFLAGS "${QUICHE_CFLAGS}") + message(STATUS "Found Quiche (via pkg-config): ${QUICHE_INCLUDE_DIRS} (found version \"${QUICHE_VERSION}\")") +else() + find_path(QUICHE_INCLUDE_DIR NAMES "quiche.h") + find_library(QUICHE_LIBRARY NAMES "quiche") -if(PC_QUICHE_VERSION) - set(QUICHE_VERSION ${PC_QUICHE_VERSION}) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Quiche + REQUIRED_VARS + QUICHE_INCLUDE_DIR + QUICHE_LIBRARY + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Quiche - REQUIRED_VARS - QUICHE_INCLUDE_DIR - QUICHE_LIBRARY - VERSION_VAR - QUICHE_VERSION -) + if(QUICHE_FOUND) + set(QUICHE_INCLUDE_DIRS ${QUICHE_INCLUDE_DIR}) + set(QUICHE_LIBRARIES ${QUICHE_LIBRARY}) + endif() -if(QUICHE_FOUND) - set(QUICHE_INCLUDE_DIRS ${QUICHE_INCLUDE_DIR}) - set(QUICHE_LIBRARIES ${QUICHE_LIBRARY}) + mark_as_advanced(QUICHE_INCLUDE_DIR QUICHE_LIBRARY) endif() - -mark_as_advanced(QUICHE_INCLUDE_DIR QUICHE_LIBRARY) diff --git a/CMake/FindRustls.cmake b/CMake/FindRustls.cmake index ffd6859ff5f565..db0e153f3bb79d 100644 --- a/CMake/FindRustls.cmake +++ b/CMake/FindRustls.cmake @@ -21,53 +21,83 @@ # SPDX-License-Identifier: curl # ########################################################################### -# Find the rustls library +# Find the Rustls library # # Input variables: # -# RUSTLS_INCLUDE_DIR The rustls include directory -# RUSTLS_LIBRARY Path to rustls library +# - `RUSTLS_INCLUDE_DIR`: The Rustls include directory. +# - `RUSTLS_LIBRARY`: Path to `rustls` library. # # Result variables: # -# RUSTLS_FOUND System has rustls -# RUSTLS_INCLUDE_DIRS The rustls include directories -# RUSTLS_LIBRARIES The rustls library names -# RUSTLS_VERSION Version of rustls +# - `RUSTLS_FOUND`: System has Rustls. +# - `RUSTLS_INCLUDE_DIRS`: The Rustls include directories. +# - `RUSTLS_LIBRARIES`: The Rustls library names. +# - `RUSTLS_LIBRARY_DIRS`: The Rustls library directories. +# - `RUSTLS_PC_REQUIRES`: The Rustls pkg-config packages. +# - `RUSTLS_CFLAGS`: Required compiler flags. +# - `RUSTLS_VERSION`: Version of Rustls. -if(CURL_USE_PKGCONFIG) +if(CURL_USE_PKGCONFIG AND + NOT DEFINED RUSTLS_INCLUDE_DIR AND + NOT DEFINED RUSTLS_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_RUSTLS "rustls") + pkg_check_modules(RUSTLS "rustls") endif() -find_path(RUSTLS_INCLUDE_DIR NAMES "rustls.h" - HINTS - ${PC_RUSTLS_INCLUDEDIR} - ${PC_RUSTLS_INCLUDE_DIRS} -) +if(RUSTLS_FOUND) + set(RUSTLS_PC_REQUIRES "rustls") + string(REPLACE ";" " " RUSTLS_CFLAGS "${RUSTLS_CFLAGS}") + message(STATUS "Found Rustls (via pkg-config): ${RUSTLS_INCLUDE_DIRS} (found version \"${RUSTLS_VERSION}\")") +else() + find_path(RUSTLS_INCLUDE_DIR NAMES "rustls.h") + find_library(RUSTLS_LIBRARY NAMES "rustls") + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Rustls + REQUIRED_VARS + RUSTLS_INCLUDE_DIR + RUSTLS_LIBRARY + ) -find_library(RUSTLS_LIBRARY NAMES "rustls" - HINTS - ${PC_RUSTLS_LIBDIR} - ${PC_RUSTLS_LIBRARY_DIRS} -) + if(RUSTLS_FOUND) + set(RUSTLS_INCLUDE_DIRS ${RUSTLS_INCLUDE_DIR}) + set(RUSTLS_LIBRARIES ${RUSTLS_LIBRARY}) + endif() -if(PC_RUSTLS_VERSION) - set(RUSTLS_VERSION ${PC_RUSTLS_VERSION}) + mark_as_advanced(RUSTLS_INCLUDE_DIR RUSTLS_LIBRARY) endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Rustls - REQUIRED_VARS - RUSTLS_INCLUDE_DIR - RUSTLS_LIBRARY - VERSION_VAR - RUSTLS_VERSION -) +if(APPLE) + find_library(SECURITY_FRAMEWORK "Security") + mark_as_advanced(SECURITY_FRAMEWORK) + if(NOT SECURITY_FRAMEWORK) + message(FATAL_ERROR "Security framework not found") + endif() + list(APPEND RUSTLS_LIBRARIES "-framework Security") -if(RUSTLS_FOUND) - set(RUSTLS_INCLUDE_DIRS ${RUSTLS_INCLUDE_DIR}) - set(RUSTLS_LIBRARIES ${RUSTLS_LIBRARY}) -endif() + find_library(FOUNDATION_FRAMEWORK "Foundation") + mark_as_advanced(FOUNDATION_FRAMEWORK) + if(NOT FOUNDATION_FRAMEWORK) + message(FATAL_ERROR "Foundation framework not found") + endif() + list(APPEND RUSTLS_LIBRARIES "-framework Foundation") +elseif(NOT WIN32) + find_library(_pthread_library "pthread") + if(_pthread_library) + list(APPEND RUSTLS_LIBRARIES "pthread") + endif() + mark_as_advanced(_pthread_library) + + find_library(_dl_library "dl") + if(_dl_library) + list(APPEND RUSTLS_LIBRARIES "dl") + endif() + mark_as_advanced(_dl_library) -mark_as_advanced(RUSTLS_INCLUDE_DIR RUSTLS_LIBRARY) + find_library(_math_library "m") + if(_math_library) + list(APPEND RUSTLS_LIBRARIES "m") + endif() + mark_as_advanced(_math_library) +endif() diff --git a/CMake/FindWolfSSH.cmake b/CMake/FindWolfSSH.cmake index 608e3abfc5cacb..98de656b923a31 100644 --- a/CMake/FindWolfSSH.cmake +++ b/CMake/FindWolfSSH.cmake @@ -21,23 +21,24 @@ # SPDX-License-Identifier: curl # ########################################################################### -# Find the wolfssh library +# Find the wolfSSH library # # Input variables: # -# WOLFSSH_INCLUDE_DIR The wolfssh include directory -# WOLFSSH_LIBRARY Path to wolfssh library +# - `WOLFSSH_INCLUDE_DIR`: The wolfSSH include directory. +# - `WOLFSSH_LIBRARY`: Path to `wolfssh` library. # # Result variables: # -# WOLFSSH_FOUND System has wolfssh -# WOLFSSH_INCLUDE_DIRS The wolfssh include directories -# WOLFSSH_LIBRARIES The wolfssh library names -# WOLFSSH_VERSION Version of wolfssh +# - `WOLFSSH_FOUND`: System has wolfSSH. +# - `WOLFSSH_INCLUDE_DIRS`: The wolfSSH include directories. +# - `WOLFSSH_LIBRARIES`: The wolfSSH library names. +# - `WOLFSSH_VERSION`: Version of wolfSSH. find_path(WOLFSSH_INCLUDE_DIR NAMES "wolfssh/ssh.h") find_library(WOLFSSH_LIBRARY NAMES "wolfssh" "libwolfssh") +unset(WOLFSSH_VERSION CACHE) if(WOLFSSH_INCLUDE_DIR AND EXISTS "${WOLFSSH_INCLUDE_DIR}/wolfssh/version.h") set(_version_regex "#[\t ]*define[\t ]+LIBWOLFSSH_VERSION_STRING[\t ]+\"([^\"]*)\"") file(STRINGS "${WOLFSSH_INCLUDE_DIR}/wolfssh/version.h" _version_str REGEX "${_version_regex}") diff --git a/CMake/FindWolfSSL.cmake b/CMake/FindWolfSSL.cmake index 905fbfd5d6b005..8f6f96462f707f 100644 --- a/CMake/FindWolfSSL.cmake +++ b/CMake/FindWolfSSL.cmake @@ -21,21 +21,21 @@ # SPDX-License-Identifier: curl # ########################################################################### -# Find the wolfssl library +# Find the wolfSSL library # # Input variables: # -# WOLFSSL_INCLUDE_DIR The wolfssl include directory -# WolfSSL_INCLUDE_DIR The wolfssl include directory (deprecated) -# WOLFSSL_LIBRARY Path to wolfssl library -# WolfSSL_LIBRARY Path to wolfssl library (deprecated) +# - `WOLFSSL_INCLUDE_DIR`: The wolfSSL include directory. +# - `WOLFSSL_LIBRARY`: Path to `wolfssl` library. # # Result variables: # -# WOLFSSL_FOUND System has wolfssl -# WOLFSSL_INCLUDE_DIRS The wolfssl include directories -# WOLFSSL_LIBRARIES The wolfssl library names -# WOLFSSL_VERSION Version of wolfssl +# - `WOLFSSL_FOUND`: System has wolfSSL. +# - `WOLFSSL_INCLUDE_DIRS`: The wolfSSL include directories. +# - `WOLFSSL_LIBRARIES`: The wolfSSL library names. +# - `WOLFSSL_LIBRARY_DIRS`: The wolfSSL library directories. +# - `WOLFSSL_CFLAGS`: Required compiler flags. +# - `WOLFSSL_VERSION`: Version of wolfSSL. if(DEFINED WolfSSL_INCLUDE_DIR AND NOT DEFINED WOLFSSL_INCLUDE_DIR) message(WARNING "WolfSSL_INCLUDE_DIR is deprecated, use WOLFSSL_INCLUDE_DIR instead.") @@ -46,53 +46,51 @@ if(DEFINED WolfSSL_LIBRARY AND NOT DEFINED WOLFSSL_LIBRARY) set(WOLFSSL_LIBRARY "${WolfSSL_LIBRARY}") endif() -if(CURL_USE_PKGCONFIG) +if(CURL_USE_PKGCONFIG AND + NOT DEFINED WOLFSSL_INCLUDE_DIR AND + NOT DEFINED WOLFSSL_LIBRARY) find_package(PkgConfig QUIET) - pkg_check_modules(PC_WOLFSSL "wolfssl") + pkg_check_modules(WOLFSSL "wolfssl") endif() -find_path(WOLFSSL_INCLUDE_DIR NAMES "wolfssl/ssl.h" - HINTS - ${PC_WOLFSSL_INCLUDEDIR} - ${PC_WOLFSSL_INCLUDE_DIRS} -) +if(WOLFSSL_FOUND) + string(REPLACE ";" " " WOLFSSL_CFLAGS "${WOLFSSL_CFLAGS}") + message(STATUS "Found WolfSSL (via pkg-config): ${WOLFSSL_INCLUDE_DIRS} (found version \"${WOLFSSL_VERSION}\")") +else() + find_path(WOLFSSL_INCLUDE_DIR NAMES "wolfssl/ssl.h") + find_library(WOLFSSL_LIBRARY NAMES "wolfssl") -find_library(WOLFSSL_LIBRARY NAMES "wolfssl" - HINTS - ${PC_WOLFSSL_LIBDIR} - ${PC_WOLFSSL_LIBRARY_DIRS} -) + unset(WOLFSSL_VERSION CACHE) + if(WOLFSSL_INCLUDE_DIR AND EXISTS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h") + set(_version_regex "#[\t ]*define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"([^\"]*)\"") + file(STRINGS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h" _version_str REGEX "${_version_regex}") + string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") + set(WOLFSSL_VERSION "${_version_str}") + unset(_version_regex) + unset(_version_str) + endif() -if(PC_WOLFSSL_VERSION) - set(WOLFSSL_VERSION ${PC_WOLFSSL_VERSION}) -elseif(WOLFSSL_INCLUDE_DIR AND EXISTS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h") - set(_version_regex "#[\t ]*define[\t ]+LIBWOLFSSL_VERSION_STRING[\t ]+\"([^\"]*)\"") - file(STRINGS "${WOLFSSL_INCLUDE_DIR}/wolfssl/version.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}") - set(WOLFSSL_VERSION "${_version_str}") - unset(_version_regex) - unset(_version_str) -endif() + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(WolfSSL + REQUIRED_VARS + WOLFSSL_INCLUDE_DIR + WOLFSSL_LIBRARY + VERSION_VAR + WOLFSSL_VERSION + ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(WolfSSL - REQUIRED_VARS - WOLFSSL_INCLUDE_DIR - WOLFSSL_LIBRARY - VERSION_VAR - WOLFSSL_VERSION -) + if(WOLFSSL_FOUND) + set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR}) + set(WOLFSSL_LIBRARIES ${WOLFSSL_LIBRARY}) + endif() -if(WOLFSSL_FOUND) - set(WOLFSSL_INCLUDE_DIRS ${WOLFSSL_INCLUDE_DIR}) - set(WOLFSSL_LIBRARIES ${WOLFSSL_LIBRARY}) + mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY) +endif() - if(NOT WIN32) - find_library(_math_library "m") - if(_math_library) - list(APPEND WOLFSSL_LIBRARIES "m") # for log and pow - endif() +if(NOT WIN32) + find_library(_math_library "m") + if(_math_library) + list(APPEND WOLFSSL_LIBRARIES "m") # for log and pow endif() + mark_as_advanced(_math_library) endif() - -mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY) diff --git a/CMake/FindZstd.cmake b/CMake/FindZstd.cmake index 10558ccc49ee34..7b0f5575ff2c09 100644 --- a/CMake/FindZstd.cmake +++ b/CMake/FindZstd.cmake @@ -25,17 +25,15 @@ # # Input variables: # -# ZSTD_INCLUDE_DIR The zstd include directory -# Zstd_INCLUDE_DIR The zstd include directory (deprecated) -# ZSTD_LIBRARY Path to zstd library -# Zstd_LIBRARY Path to zstd library (deprecated) +# - `ZSTD_INCLUDE_DIR`: The zstd include directory. +# - `ZSTD_LIBRARY`: Path to `zstd` library. # # Result variables: # -# ZSTD_FOUND System has zstd -# ZSTD_INCLUDE_DIRS The zstd include directories -# ZSTD_LIBRARIES The zstd library names -# ZSTD_VERSION Version of zstd +# - `ZSTD_FOUND`: System has zstd. +# - `ZSTD_INCLUDE_DIRS`: The zstd include directories. +# - `ZSTD_LIBRARIES`: The zstd library names. +# - `ZSTD_VERSION`: Version of zstd. if(DEFINED Zstd_INCLUDE_DIR AND NOT DEFINED ZSTD_INCLUDE_DIR) message(WARNING "Zstd_INCLUDE_DIR is deprecated, use ZSTD_INCLUDE_DIR instead.") diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index 4420bece0989fc..7877f4c9037053 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -30,8 +30,7 @@ macro(check_include_file_concat _file _variable) check_include_files("${CURL_INCLUDES};${_file}" ${_variable}) if(${_variable}) - set(CURL_INCLUDES ${CURL_INCLUDES} ${_file}) - set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D${_variable}") + list(APPEND CURL_INCLUDES ${_file}) endif() endmacro() @@ -39,8 +38,7 @@ endmacro() # Return result in variable: CURL_TEST_OUTPUT macro(curl_internal_test _curl_test) if(NOT DEFINED "${_curl_test}") - set(_macro_check_function_definitions - "-D${_curl_test} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS}") + string(REPLACE ";" " " _cmake_required_definitions "${CMAKE_REQUIRED_DEFINITIONS}") if(CMAKE_REQUIRED_LIBRARIES) set(_curl_test_add_libraries "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") @@ -48,10 +46,10 @@ macro(curl_internal_test _curl_test) message(STATUS "Performing Test ${_curl_test}") try_compile(${_curl_test} - ${CMAKE_BINARY_DIR} + ${PROJECT_BINARY_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c" CMAKE_FLAGS - "-DCOMPILE_DEFINITIONS:STRING=${_macro_check_function_definitions}" + "-DCOMPILE_DEFINITIONS:STRING=-D${_curl_test} ${CURL_TEST_DEFINES} ${_cmake_required_definitions}" "${_curl_test_add_libraries}" OUTPUT_VARIABLE CURL_TEST_OUTPUT) if(${_curl_test}) @@ -74,3 +72,11 @@ macro(curl_dependency_option _dependency) find_package(${_dependency} REQUIRED) endif() endmacro() + +# Convert the passed paths to libpath linker options and add them to CMAKE_REQUIRED_LINK_OPTIONS. +macro(curl_required_libpaths _libpaths_arg) + set(_libpaths "${_libpaths_arg}") + foreach(_libpath IN LISTS _libpaths) + list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "${CMAKE_LIBRARY_PATH_FLAG}${_libpath}") + endforeach() +endmacro() diff --git a/CMake/OtherTests.cmake b/CMake/OtherTests.cmake index 5e8f03f1d04e02..62eef12d711f42 100644 --- a/CMake/OtherTests.cmake +++ b/CMake/OtherTests.cmake @@ -32,20 +32,21 @@ macro(add_header_include _check _header) endif() endmacro() -set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(_cmake_try_compile_target_type_save ${CMAKE_TRY_COMPILE_TARGET_TYPE}) +set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE) + cmake_push_check_state() unset(CMAKE_EXTRA_INCLUDE_FILES) if(WIN32) set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") - set(CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") set(CMAKE_REQUIRED_LIBRARIES "ws2_32") elseif(HAVE_SYS_SOCKET_H) set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") endif() check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE) set(HAVE_STRUCT_SOCKADDR_STORAGE ${HAVE_SIZEOF_STRUCT_SOCKADDR_STORAGE}) - set(CMAKE_EXTRA_INCLUDE_FILES "") + cmake_pop_check_state() endif() if(NOT WIN32) @@ -74,7 +75,8 @@ check_c_source_compiles("${_source_epilogue} return 0; }" HAVE_STRUCT_TIMEVAL) -unset(CMAKE_TRY_COMPILE_TARGET_TYPE) +set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_cmake_try_compile_target_type_save}) +unset(_cmake_try_compile_target_type_save) # Detect HAVE_GETADDRINFO_THREADSAFE @@ -90,7 +92,7 @@ elseif(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS") set(HAVE_GETADDRINFO_THREADSAFE TRUE) -elseif(CMAKE_SYSTEM_NAME MATCHES "BSD") +elseif(BSD OR CMAKE_SYSTEM_NAME MATCHES "BSD") set(HAVE_GETADDRINFO_THREADSAFE FALSE) endif() @@ -150,3 +152,5 @@ if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW) return 0; }" HAVE_CLOCK_GETTIME_MONOTONIC_RAW) endif() + +unset(_source_epilogue) diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index 57b176c3fed41c..b62f97f10a7325 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -23,24 +23,27 @@ ########################################################################### include(CheckCCompilerFlag) -unset(WPICKY) +unset(_picky) if(CURL_WERROR AND ((CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0 AND NOT CMAKE_VERSION VERSION_LESS 3.23.0) OR # to avoid check_symbol_exists() conflicting with GCC -pedantic-errors CMAKE_C_COMPILER_ID MATCHES "Clang")) - set(WPICKY "${WPICKY} -pedantic-errors") + list(APPEND _picky "-pedantic-errors") + if(MSVC) # clang-cl + list(APPEND _picky "-Wno-language-extension-token") # Override default error to make __int64 size detection pass + endif() endif() if(APPLE AND (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3)) - set(WPICKY "${WPICKY} -Werror=partial-availability") # clang 3.6 appleclang 6.3 + list(APPEND _picky "-Werror=partial-availability") # clang 3.6 appleclang 6.3 endif() if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") - set(WPICKY "${WPICKY} -Werror-implicit-function-declaration") # clang 1.0 gcc 2.95 + list(APPEND _picky "-Werror-implicit-function-declaration") # clang 1.0 gcc 2.95 endif() if(PICKY_COMPILER) @@ -49,29 +52,29 @@ if(PICKY_COMPILER) # https://clang.llvm.org/docs/DiagnosticsReference.html # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html - # WPICKY_ENABLE = Options we want to enable as-is. - # WPICKY_DETECT = Options we want to test first and enable if available. + # _picky_enable = Options we want to enable as-is. + # _picky_detect = Options we want to test first and enable if available. # Prefer the -Wextra alias with clang. if(CMAKE_C_COMPILER_ID MATCHES "Clang") - set(WPICKY_ENABLE "-Wextra") + set(_picky_enable "-Wextra") else() - set(WPICKY_ENABLE "-W") + set(_picky_enable "-W") endif() - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Wall -pedantic ) # ---------------------------------- # Add new options here, if in doubt: # ---------------------------------- - set(WPICKY_DETECT + set(_picky_detect ) # Assume these options always exist with both clang and gcc. # Require clang 3.0 / gcc 2.95 or later. - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Wbad-function-cast # clang 2.7 gcc 2.95 -Wconversion # clang 2.7 gcc 2.95 -Winline # clang 1.0 gcc 1.0 @@ -89,7 +92,7 @@ if(PICKY_COMPILER) ) # Always enable with clang, version dependent with gcc - set(WPICKY_COMMON_OLD + set(_picky_common_old -Waddress # clang 2.7 gcc 4.3 -Wattributes # clang 2.7 gcc 4.1 -Wcast-align # clang 1.0 gcc 4.2 @@ -118,7 +121,7 @@ if(PICKY_COMPILER) -Wvla # clang 2.8 gcc 4.3 ) - set(WPICKY_COMMON + set(_picky_common -Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.3 -Wenum-conversion # clang 3.2 gcc 10.0 appleclang 4.6 g++ 11.0 -Wpragmas # clang 3.5 gcc 4.1 appleclang 6.0 @@ -126,51 +129,55 @@ if(PICKY_COMPILER) ) if(CMAKE_C_COMPILER_ID MATCHES "Clang") - list(APPEND WPICKY_ENABLE - ${WPICKY_COMMON_OLD} + list(APPEND _picky_enable + ${_picky_common_old} -Wshift-sign-overflow # clang 2.9 -Wshorten-64-to-32 # clang 1.0 - -Wlanguage-extension-token # clang 3.0 -Wformat=2 # clang 3.0 gcc 4.8 ) + if(NOT MSVC) + list(APPEND _picky_enable + -Wlanguage-extension-token # clang 3.0 # Avoid for clang-cl to allow __int64 + ) + endif() # Enable based on compiler version if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3)) - list(APPEND WPICKY_ENABLE - ${WPICKY_COMMON} - -Wunreachable-code-break # clang 3.5 appleclang 6.0 + list(APPEND _picky_enable + ${_picky_common} + # -Wunreachable-code-break # clang 3.5 appleclang 6.0 # Not used: Silent in "unity" builds -Wheader-guard # clang 3.4 appleclang 5.1 -Wsometimes-uninitialized # clang 3.2 appleclang 4.6 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.3)) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Wcomma # clang 3.9 appleclang 8.3 -Wmissing-variable-declarations # clang 3.2 appleclang 4.6 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.3)) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Wassign-enum # clang 7.0 appleclang 10.3 -Wextra-semi-stmt # clang 7.0 appleclang 10.3 ) endif() if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0) OR (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 12.4)) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Wimplicit-fallthrough # clang 4.0 gcc 7.0 appleclang 12.4 # We do silencing for clang 10.0 and above only ) endif() else() # gcc - list(APPEND WPICKY_DETECT - ${WPICKY_COMMON} + list(APPEND _picky_detect + ${_picky_common} ) # Enable based on compiler version if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3) - list(APPEND WPICKY_ENABLE - ${WPICKY_COMMON_OLD} + list(APPEND _picky_enable + ${_picky_common_old} -Wclobbered # gcc 4.3 -Wmissing-parameter-type # gcc 4.3 -Wold-style-declaration # gcc 4.3 @@ -179,22 +186,22 @@ if(PICKY_COMPILER) ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5 AND MINGW) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Wno-pedantic-ms-format # gcc 4.5 (MinGW-only) ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Wformat=2 # clang 3.0 gcc 4.8 ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Warray-bounds=2 -ftree-vrp # clang 3.0 gcc 5.0 (clang default: -Warray-bounds) ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.0) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Wduplicated-cond # gcc 6.0 -Wnull-dereference # clang 3.0 gcc 6.0 (clang default) -fdelete-null-pointer-checks @@ -203,7 +210,7 @@ if(PICKY_COMPILER) ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Walloc-zero # gcc 7.0 -Wduplicated-branches # gcc 7.0 -Wformat-truncation=2 # gcc 7.0 @@ -212,7 +219,7 @@ if(PICKY_COMPILER) ) endif() if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0) - list(APPEND WPICKY_ENABLE + list(APPEND _picky_enable -Warith-conversion # gcc 10.0 ) endif() @@ -220,26 +227,39 @@ if(PICKY_COMPILER) # - foreach(_ccopt IN LISTS WPICKY_ENABLE) - set(WPICKY "${WPICKY} ${_ccopt}") + foreach(_ccopt IN LISTS _picky_enable) + list(APPEND _picky "${_ccopt}") endforeach() - foreach(_ccopt IN LISTS WPICKY_DETECT) - # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new - # test result in. + foreach(_ccopt IN LISTS _picky_detect) + # Use a unique variable name 1. for meaningful log output 2. to have a fresh, undefined variable for each detection string(MAKE_C_IDENTIFIER "OPT${_ccopt}" _optvarname) # GCC only warns about unknown -Wno- options if there are also other diagnostic messages, # so test for the positive form instead string(REPLACE "-Wno-" "-W" _ccopt_on "${_ccopt}") check_c_compiler_flag(${_ccopt_on} ${_optvarname}) if(${_optvarname}) - set(WPICKY "${WPICKY} ${_ccopt}") + list(APPEND _picky "${_ccopt}") endif() endforeach() endif() endif() -if(WPICKY) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WPICKY}") - message(STATUS "Picky compiler options:${WPICKY}") +# clang-cl +if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND MSVC) + if(CMAKE_VERSION VERSION_LESS 3.12) + set(_picky_tmp "") + foreach(_ccopt IN LISTS _picky) + list(APPEND _picky_tmp "/clang:${_ccopt}") + endforeach() + set(_picky ${_picky_tmp}) + else() + list(TRANSFORM _picky PREPEND "/clang:") + endif() +endif() + +if(_picky) + string(REPLACE ";" " " _picky "${_picky}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_picky}") + message(STATUS "Picky compiler options: ${_picky}") endif() diff --git a/CMake/Platforms/WindowsCache.cmake b/CMake/Platforms/WindowsCache.cmake index 1cedad3fd79d93..077bed228e95e3 100644 --- a/CMake/Platforms/WindowsCache.cmake +++ b/CMake/Platforms/WindowsCache.cmake @@ -102,13 +102,12 @@ set(HAVE_IF_NAMETOINDEX 0) set(HAVE_GETRLIMIT 0) set(HAVE_SETRLIMIT 0) set(HAVE_FSETXATTR 0) -set(HAVE_LIBSOCKET 0) set(HAVE_SETLOCALE 1) set(HAVE_SETMODE 1) +set(HAVE__SETMODE 1) set(HAVE_GETPEERNAME 1) set(HAVE_GETSOCKNAME 1) set(HAVE_GETHOSTNAME 1) -set(HAVE_LIBZ 0) set(HAVE_RECV 1) set(HAVE_SEND 1) @@ -123,6 +122,7 @@ set(HAVE_IFADDRS_H 0) set(HAVE_IO_H 1) set(HAVE_NETDB_H 0) set(HAVE_NETINET_IN_H 0) +set(HAVE_NETINET_IN6_H 0) set(HAVE_NETINET_TCP_H 0) set(HAVE_NETINET_UDP_H 0) set(HAVE_NET_IF_H 0) @@ -148,7 +148,6 @@ set(HAVE_TERMIO_H 0) set(HAVE_LINUX_TCP_H 0) set(HAVE_FSEEKO 0) # mingw-w64 2.0.0 and newer has it -set(HAVE__FSEEKI64 1) set(HAVE_SOCKET 1) set(HAVE_SELECT 1) set(HAVE_STRDUP 1) @@ -167,7 +166,6 @@ set(HAVE_GETHOSTBYNAME_R 0) set(HAVE_SIGNAL 1) set(HAVE_SIGACTION 0) set(HAVE_GLIBC_STRERROR_R 0) -set(HAVE_MACH_ABSOLUTE_TIME 0) set(HAVE_GETIFADDRS 0) set(HAVE_FCNTL_O_NONBLOCK 0) set(HAVE_IOCTLSOCKET 1) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0129380e660458..52aa1177c6c484 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,20 +48,32 @@ message(STATUS "Using CMake version ${CMAKE_VERSION}") # Collect command-line arguments for buildinfo.txt. # Must reside at the top of the script to work as expected. -get_cmake_property(_cache_vars CACHE_VARIABLES) -unset(_cmake_args) -foreach(_cache_var ${_cache_vars}) - get_property(_cache_var_helpstring CACHE ${_cache_var} PROPERTY HELPSTRING) - if(_cache_var_helpstring STREQUAL "No help, variable specified on the command line.") - get_property(_cache_var_type CACHE ${_cache_var} PROPERTY TYPE) - if(_cache_var_type STREQUAL "UNINITIALIZED") - set(_cache_var_type) - else() - set(_cache_var_type ":${_cache_var_type}") +set(_cmake_args "") +if(NOT "$ENV{CURL_BUILDINFO}$ENV{CURL_CI}$ENV{CI}" STREQUAL "") + get_cmake_property(_cache_vars CACHE_VARIABLES) + foreach(_cache_var IN ITEMS ${_cache_vars}) + get_property(_cache_var_helpstring CACHE ${_cache_var} PROPERTY HELPSTRING) + if(_cache_var_helpstring STREQUAL "No help, variable specified on the command line.") + get_property(_cache_var_type CACHE ${_cache_var} PROPERTY TYPE) + get_property(_cache_var_value CACHE ${_cache_var} PROPERTY VALUE) + if(_cache_var_type STREQUAL "UNINITIALIZED") + set(_cache_var_type) + else() + set(_cache_var_type ":${_cache_var_type}") + endif() + set(_cmake_args "${_cmake_args} -D${_cache_var}${_cache_var_type}=\"${_cache_var_value}\"") endif() - set(_cmake_args "${_cmake_args} -D${_cache_var}${_cache_var_type}=\"${${_cache_var}}\"") - endif() -endforeach() + endforeach() +endif() + +function(curl_dumpvars) # Dump all defined variables with their values + message("::group::CMake Variable Dump") + get_cmake_property(_vars VARIABLES) + foreach(_var ${_vars}) + message("${_var} = ${${_var}}") + endforeach() + message("::endgroup::") +endfunction() set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}") include(Utilities) @@ -69,7 +81,19 @@ include(Macros) include(CMakeDependentOption) include(CheckCCompilerFlag) -project(CURL C) +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/curl/curlver.h" _curl_version_h_contents REGEX "#define LIBCURL_VERSION( |_NUM )") +string(REGEX MATCH "#define LIBCURL_VERSION \"[^\"]*" _curl_version ${_curl_version_h_contents}) +string(REGEX REPLACE "[^\"]+\"" "" _curl_version ${_curl_version}) +string(REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+" _curl_version_num ${_curl_version_h_contents}) +string(REGEX REPLACE "[^0]+0x" "" _curl_version_num ${_curl_version_num}) +unset(_curl_version_h_contents) + +message(STATUS "curl version=[${_curl_version}]") + +string(REGEX REPLACE "([0-9]+\.[0-9]+\.[0-9]+).+" "\\1" _curl_version_sem "${_curl_version}") +project(CURL + VERSION "${_curl_version_sem}" + LANGUAGES C) unset(_target_flags) if(APPLE) @@ -78,6 +102,9 @@ endif() if(UNIX) set(_target_flags "${_target_flags} UNIX") endif() +if(BSD) + set(_target_flags "${_target_flags} BSD") +endif() if(WIN32) set(_target_flags "${_target_flags} WIN32") endif() @@ -110,36 +137,29 @@ if(CMAKE_CROSSCOMPILING) "${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}") endif() -function(curl_dumpvars) # Dump all defined variables with their values - message("::group::CMake Variable Dump") - get_cmake_property(_vars VARIABLES) - foreach(_var ${_vars}) - message("${_var} = ${${_var}}") - endforeach() - message("::endgroup::") -endfunction() - -file(STRINGS "${CURL_SOURCE_DIR}/include/curl/curlver.h" _curl_version_h_contents REGEX "#define LIBCURL_VERSION( |_NUM )") -string(REGEX MATCH "#define LIBCURL_VERSION \"[^\"]*" CURL_VERSION ${_curl_version_h_contents}) -string(REGEX REPLACE "[^\"]+\"" "" CURL_VERSION ${CURL_VERSION}) -string(REGEX MATCH "#define LIBCURL_VERSION_NUM 0x[0-9a-fA-F]+" CURL_VERSION_NUM ${_curl_version_h_contents}) -string(REGEX REPLACE "[^0]+0x" "" CURL_VERSION_NUM ${CURL_VERSION_NUM}) -unset(_curl_version_h_contents) - -message(STATUS "curl version=[${CURL_VERSION}]") - if(CMAKE_C_COMPILER_TARGET) - set(OS "\"${CMAKE_C_COMPILER_TARGET}\"") + set(CURL_OS "\"${CMAKE_C_COMPILER_TARGET}\"") else() - set(OS "\"${CMAKE_SYSTEM_NAME}\"") + set(CURL_OS "\"${CMAKE_SYSTEM_NAME}\"") endif() -include_directories("${CURL_SOURCE_DIR}/include") +include_directories("${PROJECT_SOURCE_DIR}/include") if(NOT DEFINED CMAKE_UNITY_BUILD_BATCH_SIZE) set(CMAKE_UNITY_BUILD_BATCH_SIZE 0) endif() +# Having CMAKE_TRY_COMPILE_TARGET_TYPE set to STATIC_LIBRARY breaks certain +# 'check_function_exists()' detections (possibly more), by detecting +# non-existing features. This happens by default when using 'ios.toolchain.cmake'. +# Work it around by setting this value to `EXECUTABLE`. +if(CMAKE_TRY_COMPILE_TARGET_TYPE STREQUAL "STATIC_LIBRARY") + message(STATUS "CMAKE_TRY_COMPILE_TARGET_TYPE was found set to STATIC_LIBRARY. " + "Overriding with EXECUTABLE for feature detections to work.") + set(_cmake_try_compile_target_type_save ${CMAKE_TRY_COMPILE_TARGET_TYPE}) + set(CMAKE_TRY_COMPILE_TARGET_TYPE "EXECUTABLE") +endif() + option(CURL_WERROR "Turn compiler warnings into errors" OFF) option(PICKY_COMPILER "Enable picky compiler options" ON) option(BUILD_CURL_EXE "Build curl executable" ON) @@ -150,8 +170,8 @@ option(ENABLE_ARES "Enable c-ares support" OFF) option(CURL_DISABLE_INSTALL "Disable installation targets" OFF) if(WIN32) - option(CURL_STATIC_CRT "Build libcurl with static CRT on Windows (/MT)" OFF) - if(CURL_STATIC_CRT) + option(CURL_STATIC_CRT "Build libcurl with static CRT with MSVC (/MT)" OFF) + if(CURL_STATIC_CRT AND MSVC) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /MT") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /MTd") @@ -165,11 +185,12 @@ if(WIN32) endif() endif() + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DWIN32_LEAN_AND_MEAN") # Apply to all feature checks + set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string") if(CURL_TARGET_WINDOWS_VERSION) add_definitions("-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") - list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") - set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") + list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}") # Apply to all feature checks endif() # Detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT @@ -196,8 +217,11 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE") # Required for sendmmsg() endif() -option(ENABLE_DEBUG "Enable curl debug features" OFF) -option(ENABLE_CURLDEBUG "Enable TrackMemory feature" ${ENABLE_DEBUG}) +option(ENABLE_DEBUG "Enable curl debug features (for developing curl itself)" OFF) +if(ENABLE_DEBUG) + message(WARNING "This curl build is Debug-enabled, do not use in production.") +endif() +option(ENABLE_CURLDEBUG "Enable TrackMemory debug feature" ${ENABLE_DEBUG}) if(MSVC) set(ENABLE_CURLDEBUG OFF) # FIXME: TrackMemory + MSVC fails test 558 and 1330. Tested with static build, Debug mode. @@ -284,7 +308,7 @@ option(CURL_DISABLE_KERBEROS_AUTH "Disable Kerberos authentication" OFF) mark_as_advanced(CURL_DISABLE_KERBEROS_AUTH) option(CURL_DISABLE_NEGOTIATE_AUTH "Disable negotiate authentication" OFF) mark_as_advanced(CURL_DISABLE_NEGOTIATE_AUTH) -option(CURL_DISABLE_AWS "Disable AWS-SIG4" OFF) +option(CURL_DISABLE_AWS "Disable aws-sigv4" OFF) mark_as_advanced(CURL_DISABLE_AWS) option(CURL_DISABLE_DICT "Disable DICT" OFF) mark_as_advanced(CURL_DISABLE_DICT) @@ -292,10 +316,6 @@ option(CURL_DISABLE_DOH "Disable DNS-over-HTTPS" OFF) mark_as_advanced(CURL_DISABLE_DOH) option(CURL_DISABLE_FILE "Disable FILE" OFF) mark_as_advanced(CURL_DISABLE_FILE) -cmake_dependent_option(CURL_DISABLE_FORM_API "Disable form-api" - OFF "NOT CURL_DISABLE_MIME" - ON) -mark_as_advanced(CURL_DISABLE_FORM_API) option(CURL_DISABLE_FTP "Disable FTP" OFF) mark_as_advanced(CURL_DISABLE_FTP) option(CURL_DISABLE_GETOPTIONS "Disable curl_easy_options API for existing options to curl_easy_setopt" OFF) @@ -320,10 +340,14 @@ option(CURL_DISABLE_LIBCURL_OPTION "Disable --libcurl option from the curl tool" mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION) option(CURL_DISABLE_MIME "Disable MIME support" OFF) mark_as_advanced(CURL_DISABLE_MIME) +cmake_dependent_option(CURL_DISABLE_FORM_API "Disable form-api" + OFF "NOT CURL_DISABLE_MIME" + ON) +mark_as_advanced(CURL_DISABLE_FORM_API) option(CURL_DISABLE_MQTT "Disable MQTT" OFF) -mark_as_advanced(CURL_DISABLE_BINDLOCAL) -option(CURL_DISABLE_BINDLOCAL "Disable local binding support" OFF) mark_as_advanced(CURL_DISABLE_MQTT) +option(CURL_DISABLE_BINDLOCAL "Disable local binding support" OFF) +mark_as_advanced(CURL_DISABLE_BINDLOCAL) option(CURL_DISABLE_NETRC "Disable netrc parser" OFF) mark_as_advanced(CURL_DISABLE_NETRC) option(CURL_DISABLE_NTLM "Disable NTLM support" OFF) @@ -349,9 +373,9 @@ mark_as_advanced(CURL_DISABLE_SMB) option(CURL_DISABLE_SMTP "Disable SMTP" OFF) mark_as_advanced(CURL_DISABLE_SMTP) option(CURL_DISABLE_SOCKETPAIR "Disable use of socketpair for curl_multi_poll" OFF) -option(CURL_DISABLE_WEBSOCKETS "Disable WebSockets" OFF) -mark_as_advanced(CURL_DISABLE_WEBSOCKETS) mark_as_advanced(CURL_DISABLE_SOCKETPAIR) +option(CURL_DISABLE_WEBSOCKETS "Disable WebSocket" OFF) +mark_as_advanced(CURL_DISABLE_WEBSOCKETS) option(CURL_DISABLE_TELNET "Disable Telnet" OFF) mark_as_advanced(CURL_DISABLE_TELNET) option(CURL_DISABLE_TFTP "Disable TFTP" OFF) @@ -462,7 +486,12 @@ include(CheckCSourceCompiles) # Preload settings on Windows if(WIN32) - include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake) + include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake") +elseif(APPLE) + # Fast-track predictable feature detections + set(HAVE_EVENTFD 0) + set(HAVE_GETPASS_R 0) + set(HAVE_SENDMMSG 0) endif() if(ENABLE_THREADED_RESOLVER) @@ -477,9 +506,11 @@ if(ENABLE_THREADED_RESOLVER) endif() # Check for all needed libraries -check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) -if(HAVE_LIBSOCKET) - set(CURL_LIBS "socket;${CURL_LIBS}") +if(NOT WIN32 AND NOT APPLE) + check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) + if(HAVE_LIBSOCKET) + set(CURL_LIBS "socket;${CURL_LIBS}") + endif() endif() check_function_exists("gethostname" HAVE_GETHOSTNAME) @@ -608,6 +639,7 @@ if(CURL_USE_OPENSSL) endif() set(_curl_ca_bundle_supported TRUE) + cmake_push_check_state() set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) if(NOT DEFINED HAVE_BORINGSSL) check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL) @@ -615,6 +647,7 @@ if(CURL_USE_OPENSSL) if(NOT DEFINED HAVE_AWSLC) check_symbol_exists("OPENSSL_IS_AWSLC" "openssl/base.h" HAVE_AWSLC) endif() + cmake_pop_check_state() endif() if(CURL_USE_MBEDTLS) @@ -622,8 +655,13 @@ if(CURL_USE_MBEDTLS) set(_ssl_enabled ON) set(USE_MBEDTLS ON) list(APPEND CURL_LIBS ${MBEDTLS_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "mbedtls") + list(APPEND CURL_LIBDIRS ${MBEDTLS_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${MBEDTLS_PC_REQUIRES}) include_directories(SYSTEM ${MBEDTLS_INCLUDE_DIRS}) + link_directories(${MBEDTLS_LIBRARY_DIRS}) + if(MBEDTLS_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MBEDTLS_CFLAGS}") + endif() if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "mbedtls") set(_valid_default_ssl_backend TRUE) @@ -651,8 +689,13 @@ if(CURL_USE_WOLFSSL) set(_ssl_enabled ON) set(USE_WOLFSSL ON) list(APPEND CURL_LIBS ${WOLFSSL_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${WOLFSSL_LIBRARY_DIRS}) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "wolfssl") include_directories(SYSTEM ${WOLFSSL_INCLUDE_DIRS}) + link_directories(${WOLFSSL_LIBRARY_DIRS}) + if(WOLFSSL_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WOLFSSL_CFLAGS}") + endif() if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "wolfssl") set(_valid_default_ssl_backend TRUE) @@ -702,8 +745,13 @@ if(CURL_USE_RUSTLS) set(_ssl_enabled ON) set(USE_RUSTLS ON) list(APPEND CURL_LIBS ${RUSTLS_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "rustls") + list(APPEND CURL_LIBDIRS ${RUSTLS_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${RUSTLS_PC_REQUIRES}) include_directories(SYSTEM ${RUSTLS_INCLUDE_DIRS}) + link_directories(${RUSTLS_LIBRARY_DIRS}) + if(RUSTLS_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${RUSTLS_CFLAGS}") + endif() if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "rustls") set(_valid_default_ssl_backend TRUE) @@ -727,7 +775,6 @@ if(ZLIB_FOUND) # get our dependencies transitively. list(APPEND CURL_LIBS ZLIB::ZLIB) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "zlib") - list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS}) endif() option(CURL_BROTLI "Use brotli" OFF) @@ -739,7 +786,6 @@ if(CURL_BROTLI) list(APPEND CURL_LIBS ${BROTLI_LIBRARIES}) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libbrotlidec") include_directories(SYSTEM ${BROTLI_INCLUDE_DIRS}) - list(APPEND CMAKE_REQUIRED_INCLUDES ${BROTLI_INCLUDE_DIRS}) endif() endif() @@ -773,6 +819,7 @@ macro(openssl_check_symbol_exists _symbol _files _variable _extra_libs) elseif(USE_WOLFSSL) set(CMAKE_REQUIRED_INCLUDES "${WOLFSSL_INCLUDE_DIRS}") set(CMAKE_REQUIRED_LIBRARIES "${WOLFSSL_LIBRARIES}") + curl_required_libpaths("${WOLFSSL_LIBRARY_DIRS}") if(HAVE_LIBZ) list(APPEND CMAKE_REQUIRED_INCLUDES "${ZLIB_INCLUDE_DIRS}") # Public wolfSSL headers require zlib headers list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}") @@ -804,6 +851,7 @@ endmacro() if(USE_WOLFSSL) openssl_check_symbol_exists("wolfSSL_DES_ecb_encrypt" "wolfssl/openssl/des.h" HAVE_WOLFSSL_DES_ECB_ENCRYPT "") + openssl_check_symbol_exists("wolfSSL_BIO_new" "wolfssl/ssl.h" HAVE_WOLFSSL_BIO "") openssl_check_symbol_exists("wolfSSL_BIO_set_shutdown" "wolfssl/ssl.h" HAVE_WOLFSSL_FULL_BIO "") endif() @@ -822,7 +870,7 @@ if(USE_ECH) if(USE_OPENSSL OR USE_WOLFSSL) # Be sure that the TLS library actually supports ECH. if(NOT DEFINED HAVE_ECH) - if(USE_OPENSSL AND HAVE_BORINGSSL) + if(USE_OPENSSL AND (HAVE_BORINGSSL OR HAVE_AWSLC)) openssl_check_symbol_exists("SSL_set1_ech_config_list" "openssl/ssl.h" HAVE_ECH "") elseif(USE_OPENSSL) openssl_check_symbol_exists("SSL_ech_set1_echconfig" "openssl/ech.h" HAVE_ECH "") @@ -831,12 +879,12 @@ if(USE_ECH) endif() endif() if(NOT HAVE_ECH) - message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/wolfSSL") + message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL") else() message(STATUS "ECH enabled.") endif() else() - message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL or wolfSSL") + message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL, AWS-LC or wolfSSL") endif() endif() @@ -893,9 +941,14 @@ if(USE_QUICHE) message(FATAL_ERROR "quiche requires BoringSSL") endif() openssl_check_quic() - include_directories(SYSTEM ${QUICHE_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${QUICHE_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${QUICHE_LIBRARY_DIRS}) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "quiche") + include_directories(SYSTEM ${QUICHE_INCLUDE_DIRS}) + link_directories(${QUICHE_LIBRARY_DIRS}) + if(QUICHE_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${QUICHE_CFLAGS}") + endif() if(NOT DEFINED HAVE_QUICHE_CONN_SET_QLOG_FD) cmake_push_check_state() set(CMAKE_REQUIRED_INCLUDES "${QUICHE_INCLUDE_DIRS}") @@ -917,9 +970,14 @@ if(USE_MSH3) openssl_check_quic() endif() find_package(MSH3 REQUIRED) - include_directories(SYSTEM ${MSH3_INCLUDE_DIRS}) list(APPEND CURL_LIBS ${MSH3_LIBRARIES}) - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libmsh3") + list(APPEND CURL_LIBDIRS ${MSH3_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${MSH3_PC_REQUIRES}) + include_directories(SYSTEM ${MSH3_INCLUDE_DIRS}) + link_directories(${MSH3_LIBRARY_DIRS}) + if(MSH3_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MSH3_CFLAGS}") + endif() endif() if(USE_OPENSSL_QUIC) @@ -954,53 +1012,65 @@ if(NOT CURL_DISABLE_LDAP) endif() endif() - set(CMAKE_LDAP_LIB "ldap" CACHE STRING "Name or full path to ldap library") - set(CMAKE_LBER_LIB "lber" CACHE STRING "Name or full path to lber library") - # Now that we know, we are not using Windows LDAP... if(NOT USE_WIN32_LDAP) + if(NOT DEFINED LDAP_LIBRARY) + set(LDAP_LIBRARY "ldap" CACHE STRING "Name or full path to ldap library") + endif() + if(NOT DEFINED LDAP_LBER_LIBRARY) + set(LDAP_LBER_LIBRARY "lber" CACHE STRING "Name or full path to lber library") + endif() + if(NOT DEFINED LDAP_INCLUDE_DIR) + set(LDAP_INCLUDE_DIR "" CACHE STRING "Path to LDAP include directory") + endif() + # Check for LDAP - set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) - check_library_exists("${CMAKE_LDAP_LIB}" "ldap_init" "" HAVE_LIBLDAP) + cmake_push_check_state() + if(USE_OPENSSL) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) + endif() + check_library_exists("${LDAP_LIBRARY}" "ldap_init" "" HAVE_LIBLDAP) if(HAVE_LIBLDAP) - check_library_exists("${CMAKE_LDAP_LIB};${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER) + check_library_exists("${LDAP_LIBRARY};${LDAP_LBER_LIBRARY}" "ber_init" "" HAVE_LIBLBER) else() - check_library_exists("${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER) + check_library_exists("${LDAP_LBER_LIBRARY}" "ber_init" "" HAVE_LIBLBER) endif() - set(CMAKE_REQUIRED_INCLUDES_BAK ${CMAKE_REQUIRED_INCLUDES}) - set(CMAKE_LDAP_INCLUDE_DIR "" CACHE STRING "Path to LDAP include directory") - if(CMAKE_LDAP_INCLUDE_DIR) - list(APPEND CMAKE_REQUIRED_INCLUDES ${CMAKE_LDAP_INCLUDE_DIR}) + if(LDAP_INCLUDE_DIR) + list(APPEND CMAKE_REQUIRED_INCLUDES ${LDAP_INCLUDE_DIR}) endif() - check_include_file_concat("ldap.h" HAVE_LDAP_H) - check_include_file_concat("lber.h" HAVE_LBER_H) + + unset(_include_list) + check_include_file("lber.h" HAVE_LBER_H) + if(HAVE_LBER_H) + list(APPEND _include_list "lber.h") + endif() + check_include_files("${_include_list};ldap.h" HAVE_LDAP_H) + unset(_include_list) if(NOT HAVE_LDAP_H) message(STATUS "LDAP_H not found CURL_DISABLE_LDAP set ON") set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE) - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_BAK}) # LDAP includes will not be used elseif(NOT HAVE_LIBLDAP) - message(STATUS "LDAP library '${CMAKE_LDAP_LIB}' not found CURL_DISABLE_LDAP set ON") + message(STATUS "LDAP library '${LDAP_LIBRARY}' not found CURL_DISABLE_LDAP set ON") set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE) - set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_BAK}) # LDAP includes will not be used else() - if(CMAKE_LDAP_INCLUDE_DIR) - include_directories(SYSTEM ${CMAKE_LDAP_INCLUDE_DIR}) + if(LDAP_INCLUDE_DIR) + include_directories(SYSTEM ${LDAP_INCLUDE_DIR}) endif() list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DLDAP_DEPRECATED=1") - list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB}) - set(CURL_LIBS "${CMAKE_LDAP_LIB};${CURL_LIBS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LDAP_LIBRARY}) + set(CURL_LIBS "${LDAP_LIBRARY};${CURL_LIBS}") + # FIXME: uncomment once pkg-config-based detection landed: https://github.com/curl/curl/pull/15273 + # set(LIBCURL_PC_REQUIRES_PRIVATE "${LDAP_PC_REQUIRES};${LIBCURL_PC_REQUIRES_PRIVATE}") if(HAVE_LIBLBER) - list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB}) - set(CURL_LIBS "${CMAKE_LBER_LIB};${CURL_LIBS}") + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LDAP_LBER_LIBRARY}) + set(CURL_LIBS "${LDAP_LBER_LIBRARY};${CURL_LIBS}") endif() check_function_exists("ldap_url_parse" HAVE_LDAP_URL_PARSE) check_function_exists("ldap_init_fd" HAVE_LDAP_INIT_FD) - unset(CMAKE_REQUIRED_LIBRARIES) - check_include_file("ldap_ssl.h" HAVE_LDAP_SSL_H) if(HAVE_LDAP_INIT_FD) @@ -1011,6 +1081,7 @@ if(NOT CURL_DISABLE_LDAP) set(HAVE_LDAP_SSL ON) endif() endif() + cmake_pop_check_state() endif() endif() @@ -1078,7 +1149,6 @@ if(CURL_USE_LIBPSL) if(LIBPSL_FOUND) list(APPEND CURL_LIBS ${LIBPSL_LIBRARIES}) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libpsl") - list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBPSL_INCLUDE_DIRS}") include_directories(SYSTEM ${LIBPSL_INCLUDE_DIRS}) set(USE_LIBPSL ON) else() @@ -1096,7 +1166,6 @@ if(CURL_USE_LIBSSH2) if(LIBSSH2_FOUND) list(APPEND CURL_LIBS ${LIBSSH2_LIBRARIES}) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "libssh2") - list(APPEND CMAKE_REQUIRED_INCLUDES "${LIBSSH2_INCLUDE_DIRS}") include_directories(SYSTEM ${LIBSSH2_INCLUDE_DIRS}) set(USE_LIBSSH2 ON) endif() @@ -1129,7 +1198,6 @@ if(NOT USE_LIBSSH2 AND NOT USE_LIBSSH AND CURL_USE_WOLFSSH) find_package(WolfSSH) if(WOLFSSH_FOUND) list(APPEND CURL_LIBS ${WOLFSSH_LIBRARIES}) - list(APPEND CMAKE_REQUIRED_INCLUDES "${WOLFSSH_INCLUDE_DIRS}") include_directories(SYSTEM ${WOLFSSH_INCLUDE_DIRS}) set(USE_WOLFSSH ON) endif() @@ -1163,52 +1231,49 @@ if(CURL_USE_GSSAPI) set(HAVE_GSSAPI ${GSS_FOUND}) if(GSS_FOUND) - list(APPEND CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIRS}) - - string(REPLACE ";" " " GSS_CFLAGS "${GSS_CFLAGS}") - string(REPLACE ";" " " GSS_LDFLAGS "${GSS_LDFLAGS}") - - foreach(_dir IN LISTS GSS_LIBRARY_DIRS) - set(GSS_LDFLAGS "${GSS_LDFLAGS} -L\"${_dir}\"") - endforeach() - - check_include_file_concat("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H) - check_include_file_concat("gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H) - check_include_file_concat("gssapi/gssapi_krb5.h" HAVE_GSSAPI_GSSAPI_KRB5_H) + if(GSS_FLAVOUR STREQUAL "GNU") + set(HAVE_GSSGNU 1) + else() + cmake_push_check_state() + list(APPEND CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIRS}) - if(GSS_FLAVOUR STREQUAL "MIT") set(_include_list "") + check_include_file("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H) if(HAVE_GSSAPI_GSSAPI_H) list(APPEND _include_list "gssapi/gssapi.h") endif() - if(HAVE_GSSAPI_GSSAPI_GENERIC_H) - list(APPEND _include_list "gssapi/gssapi_generic.h") - endif() - if(HAVE_GSSAPI_GSSAPI_KRB5_H) - list(APPEND _include_list "gssapi/gssapi_krb5.h") - endif() + check_include_files("${_include_list};gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H) - if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE) - set(CMAKE_REQUIRED_FLAGS "${GSS_CFLAGS} ${GSS_LDFLAGS}") - set(CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES}) - check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" ${_include_list} HAVE_GSS_C_NT_HOSTBASED_SERVICE) - unset(CMAKE_REQUIRED_LIBRARIES) - endif() - if(NOT HAVE_GSS_C_NT_HOSTBASED_SERVICE) - set(HAVE_OLD_GSSMIT ON) + if(GSS_FLAVOUR STREQUAL "MIT") + check_include_files("${_include_list};gssapi/gssapi_krb5.h" _have_gssapi_gssapi_krb5_h) + if(HAVE_GSSAPI_GSSAPI_GENERIC_H) + list(APPEND _include_list "gssapi/gssapi_generic.h") + endif() + if(_have_gssapi_gssapi_krb5_h) + list(APPEND _include_list "gssapi/gssapi_krb5.h") + endif() + + if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE) + set(CMAKE_REQUIRED_FLAGS ${GSS_CFLAGS}) + set(CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES}) + curl_required_libpaths("${GSS_LIBRARY_DIRS}") + check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" "${_include_list}" HAVE_GSS_C_NT_HOSTBASED_SERVICE) + endif() + if(NOT HAVE_GSS_C_NT_HOSTBASED_SERVICE) + set(HAVE_OLD_GSSMIT ON) + endif() endif() + unset(_include_list) + cmake_pop_check_state() endif() + list(APPEND CURL_LIBS ${GSS_LIBRARIES}) + list(APPEND CURL_LIBDIRS ${GSS_LIBRARY_DIRS}) + list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${GSS_PC_REQUIRES}) include_directories(SYSTEM ${GSS_INCLUDE_DIRS}) link_directories(${GSS_LIBRARY_DIRS}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_CFLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LDFLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GSS_LDFLAGS}") - list(APPEND CURL_LIBS ${GSS_LIBRARIES}) - if(GSS_FLAVOUR STREQUAL "MIT") - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "mit-krb5-gssapi") - else() # Heimdal - list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "heimdal-gssapi") + if(GSS_CFLAGS) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GSS_CFLAGS}") endif() else() message(WARNING "GSSAPI has been requested, but no supporting libraries found. Skipping.") @@ -1238,13 +1303,11 @@ endif() option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF) if(USE_LIBRTMP) - cmake_push_check_state() set(_extra_libs "rtmp") if(WIN32) list(APPEND _extra_libs "winmm") endif() openssl_check_symbol_exists("RTMP_Init" "librtmp/rtmp.h" HAVE_LIBRTMP "${_extra_libs}") - cmake_pop_check_state() if(HAVE_LIBRTMP) list(APPEND CURL_LIBS "rtmp") list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "librtmp") @@ -1276,7 +1339,7 @@ if(_curl_ca_bundle_supported) set(CURL_CA_BUNDLE "auto" CACHE STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") set(CURL_CA_FALLBACK OFF CACHE BOOL - "Set ON to use built-in CA store of TLS backend. Defaults to OFF") + "Use built-in CA store of TLS backend. Defaults to OFF") set(CURL_CA_PATH "auto" CACHE STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.") set(CURL_CA_EMBED "" CACHE @@ -1365,9 +1428,8 @@ endif() # Check for header files if(WIN32) - set(CURL_INCLUDES ${CURL_INCLUDES} "winsock2.h") - set(CURL_INCLUDES ${CURL_INCLUDES} "ws2tcpip.h") - set(CURL_INCLUDES ${CURL_INCLUDES} "windows.h") + list(APPEND CURL_INCLUDES "winsock2.h") + list(APPEND CURL_INCLUDES "ws2tcpip.h") if(HAVE_WIN32_WINNT) if(HAVE_WIN32_WINNT LESS 0x0501) @@ -1390,46 +1452,72 @@ if(WIN32) endif() endif() -check_include_file_concat("sys/eventfd.h" HAVE_SYS_EVENTFD_H) -check_include_file_concat("sys/filio.h" HAVE_SYS_FILIO_H) -check_include_file_concat("sys/wait.h" HAVE_SYS_WAIT_H) -check_include_file_concat("sys/ioctl.h" HAVE_SYS_IOCTL_H) -check_include_file_concat("sys/param.h" HAVE_SYS_PARAM_H) -check_include_file_concat("sys/poll.h" HAVE_SYS_POLL_H) -check_include_file_concat("sys/resource.h" HAVE_SYS_RESOURCE_H) +# Detect headers + +# Use check_include_file_concat() for headers required by subsequent +# check_include_file_concat() or check_symbol_exists() detections. +# Order for these is significant. +check_include_file("sys/eventfd.h" HAVE_SYS_EVENTFD_H) +check_include_file("sys/filio.h" HAVE_SYS_FILIO_H) +check_include_file("sys/wait.h" HAVE_SYS_WAIT_H) +check_include_file("sys/ioctl.h" HAVE_SYS_IOCTL_H) +check_include_file("sys/param.h" HAVE_SYS_PARAM_H) +check_include_file("sys/poll.h" HAVE_SYS_POLL_H) +check_include_file("sys/resource.h" HAVE_SYS_RESOURCE_H) check_include_file_concat("sys/select.h" HAVE_SYS_SELECT_H) check_include_file_concat("sys/socket.h" HAVE_SYS_SOCKET_H) -check_include_file_concat("sys/sockio.h" HAVE_SYS_SOCKIO_H) -check_include_file_concat("sys/stat.h" HAVE_SYS_STAT_H) +check_include_file("sys/sockio.h" HAVE_SYS_SOCKIO_H) +check_include_file("sys/stat.h" HAVE_SYS_STAT_H) check_include_file_concat("sys/time.h" HAVE_SYS_TIME_H) check_include_file_concat("sys/types.h" HAVE_SYS_TYPES_H) -check_include_file_concat("sys/un.h" HAVE_SYS_UN_H) -check_include_file_concat("sys/utime.h" HAVE_SYS_UTIME_H) -check_include_file_concat("sys/xattr.h" HAVE_SYS_XATTR_H) +check_include_file("sys/un.h" HAVE_SYS_UN_H) +check_include_file("sys/utime.h" HAVE_SYS_UTIME_H) +check_include_file("sys/xattr.h" HAVE_SYS_XATTR_H) + check_include_file_concat("arpa/inet.h" HAVE_ARPA_INET_H) -check_include_file_concat("dirent.h" HAVE_DIRENT_H) -check_include_file_concat("fcntl.h" HAVE_FCNTL_H) +check_include_file("dirent.h" HAVE_DIRENT_H) +check_include_file("fcntl.h" HAVE_FCNTL_H) check_include_file_concat("ifaddrs.h" HAVE_IFADDRS_H) -check_include_file_concat("io.h" HAVE_IO_H) +check_include_file("io.h" HAVE_IO_H) check_include_file_concat("libgen.h" HAVE_LIBGEN_H) -check_include_file_concat("locale.h" HAVE_LOCALE_H) -check_include_file_concat("net/if.h" HAVE_NET_IF_H) +check_include_file("linux/tcp.h" HAVE_LINUX_TCP_H) +check_include_file("locale.h" HAVE_LOCALE_H) +check_include_file("net/if.h" HAVE_NET_IF_H) check_include_file_concat("netdb.h" HAVE_NETDB_H) check_include_file_concat("netinet/in.h" HAVE_NETINET_IN_H) -check_include_file_concat("netinet/tcp.h" HAVE_NETINET_TCP_H) -check_include_file_concat("netinet/udp.h" HAVE_NETINET_UDP_H) -check_include_file("linux/tcp.h" HAVE_LINUX_TCP_H) - -check_include_file_concat("poll.h" HAVE_POLL_H) -check_include_file_concat("pwd.h" HAVE_PWD_H) -check_include_file_concat("stdatomic.h" HAVE_STDATOMIC_H) -check_include_file_concat("stdbool.h" HAVE_STDBOOL_H) -check_include_file_concat("strings.h" HAVE_STRINGS_H) -check_include_file_concat("stropts.h" HAVE_STROPTS_H) -check_include_file_concat("termio.h" HAVE_TERMIO_H) -check_include_file_concat("termios.h" HAVE_TERMIOS_H) +check_include_file("netinet/in6.h" HAVE_NETINET_IN6_H) +check_include_file_concat("netinet/tcp.h" HAVE_NETINET_TCP_H) # sys/types.h (e.g. Cygwin) netinet/in.h +check_include_file_concat("netinet/udp.h" HAVE_NETINET_UDP_H) # sys/types.h (e.g. Cygwin) +check_include_file("poll.h" HAVE_POLL_H) +check_include_file("pwd.h" HAVE_PWD_H) +check_include_file("stdatomic.h" HAVE_STDATOMIC_H) +check_include_file("stdbool.h" HAVE_STDBOOL_H) +check_include_file("strings.h" HAVE_STRINGS_H) +check_include_file("stropts.h" HAVE_STROPTS_H) +check_include_file("termio.h" HAVE_TERMIO_H) +check_include_file("termios.h" HAVE_TERMIOS_H) check_include_file_concat("unistd.h" HAVE_UNISTD_H) -check_include_file_concat("utime.h" HAVE_UTIME_H) +check_include_file("utime.h" HAVE_UTIME_H) + +if(CMAKE_SYSTEM_NAME MATCHES "AmigaOS") + check_include_file_concat("proto/bsdsocket.h" HAVE_PROTO_BSDSOCKET_H) +endif() + +# Pass these detection results to curl_internal_test() for use in CurlTests.c +# Add here all feature flags referenced from CurlTests.c +foreach(_variable IN ITEMS + HAVE_STDATOMIC_H + HAVE_STDBOOL_H + HAVE_STROPTS_H + HAVE_SYS_IOCTL_H + HAVE_SYS_SOCKET_H + HAVE_SYS_TYPES_H + HAVE_UNISTD_H + ) + if(${_variable}) + set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -D${_variable}") + endif() +endforeach() check_type_size("size_t" SIZEOF_SIZE_T) check_type_size("ssize_t" SIZEOF_SSIZE_T) @@ -1458,65 +1546,72 @@ endif() # Check for some functions that are used if(WIN32) - set(CMAKE_REQUIRED_LIBRARIES "ws2_32") + list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") # Apply to all feature checks elseif(HAVE_LIBSOCKET) - set(CMAKE_REQUIRED_LIBRARIES "socket") -endif() - -check_symbol_exists("fnmatch" "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH) -check_symbol_exists("basename" "${CURL_INCLUDES};string.h" HAVE_BASENAME) -check_symbol_exists("opendir" "${CURL_INCLUDES};dirent.h" HAVE_OPENDIR) -check_symbol_exists("poll" "${CURL_INCLUDES}" HAVE_POLL) -check_symbol_exists("socket" "${CURL_INCLUDES}" HAVE_SOCKET) -check_symbol_exists("sched_yield" "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD) -check_symbol_exists("socketpair" "${CURL_INCLUDES}" HAVE_SOCKETPAIR) -check_symbol_exists("recv" "${CURL_INCLUDES}" HAVE_RECV) -check_symbol_exists("send" "${CURL_INCLUDES}" HAVE_SEND) -check_symbol_exists("sendmsg" "${CURL_INCLUDES}" HAVE_SENDMSG) -check_symbol_exists("sendmmsg" "sys/socket.h" HAVE_SENDMMSG) -check_symbol_exists("select" "${CURL_INCLUDES}" HAVE_SELECT) -check_symbol_exists("strdup" "${CURL_INCLUDES};string.h" HAVE_STRDUP) -check_symbol_exists("strtok_r" "${CURL_INCLUDES};string.h" HAVE_STRTOK_R) -check_symbol_exists("strcasecmp" "${CURL_INCLUDES};string.h" HAVE_STRCASECMP) -check_symbol_exists("stricmp" "${CURL_INCLUDES};string.h" HAVE_STRICMP) -check_symbol_exists("strcmpi" "${CURL_INCLUDES};string.h" HAVE_STRCMPI) -check_symbol_exists("memrchr" "${CURL_INCLUDES};string.h" HAVE_MEMRCHR) -check_symbol_exists("alarm" "${CURL_INCLUDES}" HAVE_ALARM) -check_symbol_exists("fcntl" "${CURL_INCLUDES}" HAVE_FCNTL) -check_symbol_exists("getppid" "${CURL_INCLUDES}" HAVE_GETPPID) -check_symbol_exists("utimes" "${CURL_INCLUDES}" HAVE_UTIMES) - -check_symbol_exists("gettimeofday" "${CURL_INCLUDES}" HAVE_GETTIMEOFDAY) -check_symbol_exists("closesocket" "${CURL_INCLUDES}" HAVE_CLOSESOCKET) -check_symbol_exists("sigsetjmp" "${CURL_INCLUDES};setjmp.h" HAVE_SIGSETJMP) -check_symbol_exists("getpass_r" "${CURL_INCLUDES}" HAVE_GETPASS_R) -check_symbol_exists("getpwuid" "${CURL_INCLUDES}" HAVE_GETPWUID) -check_symbol_exists("getpwuid_r" "${CURL_INCLUDES}" HAVE_GETPWUID_R) -check_symbol_exists("geteuid" "${CURL_INCLUDES}" HAVE_GETEUID) -check_symbol_exists("utime" "${CURL_INCLUDES}" HAVE_UTIME) -check_symbol_exists("gmtime_r" "${CURL_INCLUDES};stdlib.h;time.h" HAVE_GMTIME_R) - -check_symbol_exists("gethostbyname_r" "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R) - -check_symbol_exists("signal" "${CURL_INCLUDES};signal.h" HAVE_SIGNAL) -check_symbol_exists("strtoll" "${CURL_INCLUDES};stdlib.h" HAVE_STRTOLL) -check_symbol_exists("strerror_r" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_STRERROR_R) + list(APPEND CMAKE_REQUIRED_LIBRARIES "socket") # Apply to all feature checks +endif() + +check_function_exists("fnmatch" HAVE_FNMATCH) +check_symbol_exists("basename" "${CURL_INCLUDES};string.h" HAVE_BASENAME) # libgen.h unistd.h +check_symbol_exists("opendir" "dirent.h" HAVE_OPENDIR) +check_function_exists("poll" HAVE_POLL) # poll.h +check_symbol_exists("socket" "${CURL_INCLUDES}" HAVE_SOCKET) # winsock2.h sys/socket.h +check_function_exists("sched_yield" HAVE_SCHED_YIELD) +check_symbol_exists("socketpair" "${CURL_INCLUDES}" HAVE_SOCKETPAIR) # sys/socket.h +check_symbol_exists("recv" "${CURL_INCLUDES}" HAVE_RECV) # proto/bsdsocket.h sys/types.h sys/socket.h +check_symbol_exists("send" "${CURL_INCLUDES}" HAVE_SEND) # proto/bsdsocket.h sys/types.h sys/socket.h +check_function_exists("sendmsg" HAVE_SENDMSG) +check_function_exists("sendmmsg" HAVE_SENDMMSG) +check_symbol_exists("select" "${CURL_INCLUDES}" HAVE_SELECT) # proto/bsdsocket.h sys/select.h sys/socket.h +check_symbol_exists("strdup" "string.h" HAVE_STRDUP) +check_symbol_exists("strtok_r" "string.h" HAVE_STRTOK_R) +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("memrchr" "string.h" HAVE_MEMRCHR) +check_symbol_exists("alarm" "unistd.h" HAVE_ALARM) +check_symbol_exists("fcntl" "fcntl.h" HAVE_FCNTL) +check_function_exists("getppid" HAVE_GETPPID) +check_function_exists("utimes" HAVE_UTIMES) + +check_function_exists("gettimeofday" HAVE_GETTIMEOFDAY) # sys/time.h +check_symbol_exists("closesocket" "${CURL_INCLUDES}" HAVE_CLOSESOCKET) # winsock2.h +check_symbol_exists("sigsetjmp" "setjmp.h" HAVE_SIGSETJMP) +check_function_exists("getpass_r" HAVE_GETPASS_R) +check_function_exists("getpwuid" HAVE_GETPWUID) +check_function_exists("getpwuid_r" HAVE_GETPWUID_R) +check_function_exists("geteuid" HAVE_GETEUID) +check_function_exists("utime" HAVE_UTIME) +check_symbol_exists("gmtime_r" "stdlib.h;time.h" HAVE_GMTIME_R) + +check_symbol_exists("gethostbyname_r" "netdb.h" HAVE_GETHOSTBYNAME_R) + +check_symbol_exists("signal" "signal.h" HAVE_SIGNAL) +check_symbol_exists("strtoll" "stdlib.h" HAVE_STRTOLL) +check_symbol_exists("strerror_r" "stdlib.h;string.h" HAVE_STRERROR_R) check_symbol_exists("sigaction" "signal.h" HAVE_SIGACTION) -check_symbol_exists("siginterrupt" "${CURL_INCLUDES};signal.h" HAVE_SIGINTERRUPT) -check_symbol_exists("getaddrinfo" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO) -check_symbol_exists("getifaddrs" "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) -check_symbol_exists("freeaddrinfo" "${CURL_INCLUDES}" HAVE_FREEADDRINFO) -check_symbol_exists("pipe" "${CURL_INCLUDES}" HAVE_PIPE) -check_symbol_exists("eventfd" "${CURL_INCLUDES};sys/eventfd.h" HAVE_EVENTFD) -check_symbol_exists("ftruncate" "${CURL_INCLUDES}" HAVE_FTRUNCATE) -check_symbol_exists("_fseeki64" "${CURL_INCLUDES};stdio.h" HAVE__FSEEKI64) -check_symbol_exists("getpeername" "${CURL_INCLUDES}" HAVE_GETPEERNAME) -check_symbol_exists("getsockname" "${CURL_INCLUDES}" HAVE_GETSOCKNAME) -check_symbol_exists("if_nametoindex" "${CURL_INCLUDES}" HAVE_IF_NAMETOINDEX) -check_symbol_exists("getrlimit" "${CURL_INCLUDES}" HAVE_GETRLIMIT) -check_symbol_exists("setlocale" "${CURL_INCLUDES}" HAVE_SETLOCALE) -check_symbol_exists("setmode" "${CURL_INCLUDES}" HAVE_SETMODE) -check_symbol_exists("setrlimit" "${CURL_INCLUDES}" HAVE_SETRLIMIT) +check_symbol_exists("siginterrupt" "signal.h" HAVE_SIGINTERRUPT) +check_symbol_exists("getaddrinfo" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO) # ws2tcpip.h sys/socket.h netdb.h +check_symbol_exists("getifaddrs" "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) # ifaddrs.h +check_symbol_exists("freeaddrinfo" "${CURL_INCLUDES}" HAVE_FREEADDRINFO) # ws2tcpip.h sys/socket.h netdb.h +check_function_exists("pipe" HAVE_PIPE) +check_function_exists("eventfd" HAVE_EVENTFD) +check_symbol_exists("ftruncate" "unistd.h" HAVE_FTRUNCATE) +check_symbol_exists("getpeername" "${CURL_INCLUDES}" HAVE_GETPEERNAME) # winsock2.h unistd.h proto/bsdsocket.h +check_symbol_exists("getsockname" "${CURL_INCLUDES}" HAVE_GETSOCKNAME) # winsock2.h unistd.h proto/bsdsocket.h +check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # winsock2.h net/if.h +check_function_exists("getrlimit" HAVE_GETRLIMIT) +check_function_exists("setlocale" HAVE_SETLOCALE) +check_function_exists("setmode" HAVE_SETMODE) +check_function_exists("setrlimit" HAVE_SETRLIMIT) + +if(WIN32 OR CYGWIN) + check_function_exists("_setmode" HAVE__SETMODE) +endif() + +if(CMAKE_SYSTEM_NAME MATCHES "AmigaOS") + check_symbol_exists("CloseSocket" "${CURL_INCLUDES}" HAVE_CLOSESOCKET_CAMEL) # sys/socket.h proto/bsdsocket.h +endif() if(NOT _ssl_enabled) check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM) @@ -1524,32 +1619,34 @@ endif() if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900)) # Earlier MSVC compilers had faulty snprintf implementations - check_symbol_exists("snprintf" "stdio.h" HAVE_SNPRINTF) + check_function_exists("snprintf" HAVE_SNPRINTF) endif() -check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) -check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) +if(APPLE) + check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME) +endif() +check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h if(MSVC AND (MSVC_VERSION LESS_EQUAL 1600)) set(HAVE_INET_NTOP OFF) endif() -check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) +check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h -check_symbol_exists("fsetxattr" "${CURL_INCLUDES}" HAVE_FSETXATTR) +check_symbol_exists("fsetxattr" "sys/xattr.h" HAVE_FSETXATTR) if(HAVE_FSETXATTR) curl_internal_test(HAVE_FSETXATTR_5) curl_internal_test(HAVE_FSETXATTR_6) endif() -set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") -check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T) -set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T}) -set(CMAKE_EXTRA_INCLUDE_FILES "") - +cmake_push_check_state() if(WIN32) set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY) set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY}) - set(CMAKE_EXTRA_INCLUDE_FILES "") +elseif(HAVE_SYS_SOCKET_H) + set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") + check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T) + set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T}) endif() +cmake_pop_check_state() # Do curl specific tests foreach(_curl_test IN ITEMS @@ -1577,9 +1674,10 @@ foreach(_curl_test IN ITEMS curl_internal_test(${_curl_test}) endforeach() +cmake_push_check_state() if(HAVE_FILE_OFFSET_BITS) set(_FILE_OFFSET_BITS 64) - set(CMAKE_REQUIRED_FLAGS "-D_FILE_OFFSET_BITS=64") + set(CMAKE_REQUIRED_DEFINITIONS "-D_FILE_OFFSET_BITS=64") endif() check_type_size("off_t" SIZEOF_OFF_T) @@ -1593,12 +1691,14 @@ if(HAVE_FSEEKO) endif() # Include this header to get the type -set(CMAKE_REQUIRED_INCLUDES "${CURL_SOURCE_DIR}/include") +cmake_push_check_state() +set(CMAKE_REQUIRED_INCLUDES "${PROJECT_SOURCE_DIR}/include") set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h") check_type_size("curl_off_t" SIZEOF_CURL_OFF_T) set(CMAKE_EXTRA_INCLUDE_FILES "curl/curl.h") check_type_size("curl_socket_t" SIZEOF_CURL_SOCKET_T) -set(CMAKE_EXTRA_INCLUDE_FILES "") +cmake_pop_check_state() # pop curl system headers +cmake_pop_check_state() # pop -D_FILE_OFFSET_BITS=64 if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) # On non-Windows and not cross-compiling, check for writable argv[] @@ -1612,8 +1712,6 @@ if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING) }" HAVE_WRITABLE_ARGV) endif() -unset(CMAKE_REQUIRED_FLAGS) - curl_internal_test(HAVE_GLIBC_STRERROR_R) curl_internal_test(HAVE_POSIX_STRERROR_R) @@ -1680,6 +1778,11 @@ if(CMAKE_COMPILER_IS_GNUCC AND APPLE) endif() endif() +if(_cmake_try_compile_target_type_save) + set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_cmake_try_compile_target_type_save}) + unset(_cmake_try_compile_target_type_save) +endif() + include(CMake/OtherTests.cmake) add_definitions("-DHAVE_CONFIG_H") @@ -1714,7 +1817,7 @@ if(MSVC) endif() # Use multithreaded compilation on VS 2008+ - if(MSVC_VERSION GREATER_EQUAL 1500) + if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER_EQUAL 1500) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /MP") endif() endif() @@ -1736,11 +1839,11 @@ if(CURL_LTO) cmake_policy(SET CMP0069 NEW) include(CheckIPOSupported) - check_ipo_supported(RESULT CURL_HAS_LTO OUTPUT CURL_LTO_ERROR LANGUAGES C) + check_ipo_supported(RESULT CURL_HAS_LTO OUTPUT _lto_error LANGUAGES C) if(CURL_HAS_LTO) message(STATUS "LTO supported and enabled") else() - message(FATAL_ERROR "LTO has been requested, but the compiler does not support it\n${CURL_LTO_ERROR}") + message(FATAL_ERROR "LTO has been requested, but the compiler does not support it\n${_lto_error}") endif() endif() @@ -1749,8 +1852,8 @@ endif() # (= regenerate it). function(transform_makefile_inc _input_file _output_file) file(READ ${_input_file} _makefile_inc_text) - string(REPLACE "$(top_srcdir)" "\${CURL_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text}) - string(REPLACE "$(top_builddir)" "\${CURL_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text}) + string(REPLACE "$(top_srcdir)" "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text}) + string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text}) string(REGEX REPLACE "\\\\\n" "!Ī€!Îą!" _makefile_inc_text ${_makefile_inc_text}) string(REGEX REPLACE "([a-zA-Z_][a-zA-Z0-9_]*)[\t ]*=[\t ]*([^\n]*)" "set(\\1 \\2)" _makefile_inc_text ${_makefile_inc_text}) @@ -1767,19 +1870,22 @@ endfunction() include(GNUInstallDirs) -set(CURL_INSTALL_CMAKE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") +set(_install_cmake_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") set(_project_config "${_generated_dir}/${PROJECT_NAME}Config.cmake") set(_version_config "${_generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") -cmake_dependent_option(BUILD_TESTING "Build tests" - ON "PERL_FOUND;NOT CURL_DISABLE_TESTS" - OFF) +option(BUILD_TESTING "Build tests" ON) +if(BUILD_TESTING AND PERL_FOUND AND NOT CURL_DISABLE_TESTS) + set(CURL_BUILD_TESTING ON) +else() + set(CURL_BUILD_TESTING OFF) +endif() if(HAVE_MANUAL_TOOLS) - set(CURL_MANPAGE "${CURL_BINARY_DIR}/docs/cmdline-opts/curl.1") - set(CURL_ASCIIPAGE "${CURL_BINARY_DIR}/docs/cmdline-opts/curl.txt") + set(CURL_MANPAGE "${PROJECT_BINARY_DIR}/docs/cmdline-opts/curl.1") + set(CURL_ASCIIPAGE "${PROJECT_BINARY_DIR}/docs/cmdline-opts/curl.txt") add_subdirectory(docs) endif() @@ -1794,7 +1900,7 @@ if(BUILD_EXAMPLES) add_subdirectory(docs/examples) endif() -if(BUILD_TESTING) +if(CURL_BUILD_TESTING) add_subdirectory(tests) endif() @@ -1894,7 +2000,7 @@ _add_if("MultiSSL" CURL_WITH_MULTI_SSL) _add_if("HTTPS-proxy" _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR USE_MBEDTLS OR USE_SECTRANSP OR - (USE_WOLFSSL AND HAVE_WOLFSSL_FULL_BIO))) + (USE_WOLFSSL AND HAVE_WOLFSSL_BIO))) _add_if("Unicode" ENABLE_UNICODE) _add_if("threadsafe" HAVE_ATOMIC OR (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR @@ -1945,8 +2051,8 @@ if(NOT CURL_DISABLE_INSTALL) set(CC "${CMAKE_C_COMPILER}") # TODO: probably put a -D... options here? set(CONFIGURE_OPTIONS "") - set(CURLVERSION "${CURL_VERSION}") - set(VERSIONNUM "${CURL_VERSION_NUM}") + set(CURLVERSION "${_curl_version}") + set(VERSIONNUM "${_curl_version_num}") set(prefix "${CMAKE_INSTALL_PREFIX}") set(exec_prefix "\${prefix}") if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR}) @@ -1959,7 +2065,6 @@ if(NOT CURL_DISABLE_INSTALL) else() set(libdir "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") endif() - set(LDFLAGS "${CMAKE_SHARED_LINKER_FLAGS}") # "a" (Linux) or "lib" (Windows) string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}") @@ -2040,11 +2145,12 @@ if(NOT CURL_DISABLE_INSTALL) if(LIBCURL_PC_LIBS_PRIVATE) string(REPLACE ";" " " LIBCURL_PC_LIBS_PRIVATE "${LIBCURL_PC_LIBS_PRIVATE}") endif() + set(LIBCURL_PC_LDFLAGS_PRIVATE "${CMAKE_SHARED_LINKER_FLAGS}") if(_ldflags) list(REMOVE_DUPLICATES _ldflags) string(REPLACE ";" " " _ldflags "${_ldflags}") - set(LDFLAGS "${LDFLAGS} ${_ldflags}") - string(STRIP "${LDFLAGS}" LDFLAGS) + set(LIBCURL_PC_LDFLAGS_PRIVATE "${LIBCURL_PC_LDFLAGS_PRIVATE} ${_ldflags}") + string(STRIP "${LIBCURL_PC_LDFLAGS_PRIVATE}" LIBCURL_PC_LDFLAGS_PRIVATE) endif() set(LIBCURL_PC_CFLAGS_PRIVATE "-DCURL_STATICLIB") @@ -2076,8 +2182,8 @@ if(NOT CURL_DISABLE_INSTALL) # ENABLE_STATIC # exec_prefix # includedir - # LDFLAGS # LIBCURL_PC_CFLAGS + # LIBCURL_PC_LDFLAGS_PRIVATE # LIBCURL_PC_LIBS_PRIVATE # libdir # libext @@ -2087,9 +2193,9 @@ if(NOT CURL_DISABLE_INSTALL) # SUPPORT_PROTOCOLS # VERSIONNUM configure_file( - "${CURL_SOURCE_DIR}/curl-config.in" - "${CURL_BINARY_DIR}/curl-config" @ONLY) - install(FILES "${CURL_BINARY_DIR}/curl-config" + "${PROJECT_SOURCE_DIR}/curl-config.in" + "${PROJECT_BINARY_DIR}/curl-config" @ONLY) + install(FILES "${PROJECT_BINARY_DIR}/curl-config" DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE @@ -2101,9 +2207,9 @@ if(NOT CURL_DISABLE_INSTALL) # CURLVERSION # exec_prefix # includedir - # LDFLAGS # LIBCURL_PC_CFLAGS # LIBCURL_PC_CFLAGS_PRIVATE + # LIBCURL_PC_LDFLAGS_PRIVATE # LIBCURL_PC_LIBS # LIBCURL_PC_LIBS_PRIVATE # LIBCURL_PC_REQUIRES @@ -2118,20 +2224,20 @@ if(NOT CURL_DISABLE_INSTALL) # https://manpages.debian.org/unstable/pkg-config/pkg-config.1.en.html # https://www.msys2.org/docs/pkgconfig/ configure_file( - "${CURL_SOURCE_DIR}/libcurl.pc.in" - "${CURL_BINARY_DIR}/libcurl.pc" @ONLY) - install(FILES "${CURL_BINARY_DIR}/libcurl.pc" + "${PROJECT_SOURCE_DIR}/libcurl.pc.in" + "${PROJECT_BINARY_DIR}/libcurl.pc" @ONLY) + install(FILES "${PROJECT_BINARY_DIR}/libcurl.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") # Install headers - install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl" + install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/curl" DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.h") include(CMakePackageConfigHelpers) write_basic_package_version_file( "${_version_config}" - VERSION ${CURL_VERSION} + VERSION ${_curl_version} COMPATIBILITY SameMajorVersion) file(READ "${_version_config}" _generated_version_config) file(WRITE "${_version_config}" " @@ -2149,17 +2255,17 @@ if(NOT CURL_DISABLE_INSTALL) # HAVE_LIBZ configure_package_config_file("CMake/curl-config.cmake.in" "${_project_config}" - INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR} + INSTALL_DESTINATION ${_install_cmake_dir} PATH_VARS CMAKE_INSTALL_INCLUDEDIR) if(CURL_ENABLE_EXPORT_TARGET) install(EXPORT "${TARGETS_EXPORT_NAME}" NAMESPACE "${PROJECT_NAME}::" - DESTINATION ${CURL_INSTALL_CMAKE_DIR}) + DESTINATION ${_install_cmake_dir}) endif() install(FILES ${_version_config} ${_project_config} - DESTINATION ${CURL_INSTALL_CMAKE_DIR}) + DESTINATION ${_install_cmake_dir}) # Workaround for MSVS10 to avoid the Dialog Hell # FIXME: This could be removed with future version of CMake. @@ -2186,6 +2292,11 @@ if(NOT CURL_DISABLE_INSTALL) OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + + # The `-DEV` part is important + string(REGEX REPLACE "([0-9]+\.[0-9]+)\.([0-9]+.*)" "\\2" CPACK_PACKAGE_VERSION_PATCH "${_curl_version}") + set(CPACK_GENERATOR "TGZ") + include(CPack) endif() # Save build info for test runner to pick up and log @@ -2210,7 +2321,7 @@ buildinfo.compiler: ${CMAKE_C_COMPILER_ID} buildinfo.compiler.version: ${CMAKE_C_COMPILER_VERSION} buildinfo.sysroot: ${_cmake_sysroot} ") -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/buildinfo.txt" "# This is a generated file. Do not edit.\n${_buildinfo}") +file(WRITE "${PROJECT_BINARY_DIR}/buildinfo.txt" "# This is a generated file. Do not edit.\n${_buildinfo}") if(NOT "$ENV{CURL_BUILDINFO}$ENV{CURL_CI}$ENV{CI}" STREQUAL "") message(STATUS "\n${_buildinfo}") endif() diff --git a/Dockerfile b/Dockerfile index 08944148ce90fd..6f78b0e48f8f9a 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:903d3225acecaa272bbdd7273c6c312c2af8b73644058838d23a8c9e6e5c82cf +FROM debian:bookworm-slim@sha256:c00d13c9aa5d1acfa44e5ababbb8d1f5ac53fa94bc2f993070ccea2dcaf5aa64 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \ build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \ diff --git a/Makefile.am b/Makefile.am index be3f520bba64f5..fbfb8b044730a5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,7 +69,7 @@ VC_DIST = projects/README.md \ projects/wolfssl_options.h \ projects/wolfssl_override.props -WINBUILD_DIST = winbuild/README.md winbuild/gen_resp_file.bat \ +WINBUILD_DIST = winbuild/README.md \ winbuild/MakefileBuild.vc winbuild/Makefile.vc winbuild/makedebug.bat PLAN9_DIST = plan9/include/mkfile \ diff --git a/README.md b/README.md index 7ade2d1186b58b..703e0e2a931edb 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ distribution terms. ## Backers -Thank you to all our backers! 🙏 [Become a backer](https://opencollective.com/curl#section-contribute). +Thank you to all our backers 🙏 [Become a backer](https://opencollective.com/curl#section-contribute). ## Sponsors diff --git a/RELEASE-NOTES b/RELEASE-NOTES index dc419770d0f6ec..59e66058b0dcfe 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,122 +1,53 @@ -curl and libcurl 8.11.0 +curl and libcurl 8.11.1 - Public curl releases: 262 - Command line options: 265 + Public curl releases: 263 + Command line options: 266 curl_easy_setopt() options: 306 Public functions in libcurl: 94 - Contributors: 3246 + Contributors: 3284 This release includes the following changes: - o curl: --create-dirs works for --dump-header as well [4] - o gtls: Add P12 format support [9] - o ipfs: add options to disable [8] - o WebSockets: make support official (non-experimental) [106] This release includes the following bugfixes: - o alt-svc: honor data->state.httpwant [19] - o autotools: add support for 'unity' builds, enable in CI [15] - o build: add pytest targets [71] - o build: clarify CA embed is for curl tool, mark default, improve summary [72] - o build: fix cross-compile check for poll with bionic [70] - o build: fix possible `-Wformat-overflow` in lib557 [85] - o build: limit arc4random detection to no-SSL configs [43] - o build: show if CA bundle to embed was found [83] - o build: tidy up and improve versioned-symbols options [5] - o build: tidy up deprecation suppression, enable warnings for clang [12] - o checksrc: Added checks for colon operator in ternary expressions [77] - o checksrc: check for spaces around '?', '>' and '<' [46] - o cmake, Makefile.mk: use -isystem for headers, silence BearSSL issues [37] - o cmake/FindNGTCP2: use library path as hint for finding crypto module [40] - o cmake: allow building tests in unity mode [31] - o cmake: delete unused NEED_LBER_H, HAVE_LDAP_H [38] - o cmake: disable default OpenSSL if BearSSL, GnuTLS or Rustls is enabled [44] - o cmake: drop redundant assignments [49] - o cmake: drop redundant zlib var, rename function (internals) [50] - o cmake: expand CURL_USE_PKGCONFIG to non-cross MINGW [13] - o cmake: fix broken dependency chain for cmdline-opts, tidy-ups [11] - o cmake: make `test-ci` target skip building dependencies [88] - o cmake: readd `generate-curl.1` dependency for `src` just in case [86] - o cmake: require quictls (or fork) when using msh3 on non-Windows [14] - o cmake: separate target for examples, optimize CI, fix fallouts [16] - o cmake: sync torture test parallelism with autotools [35] - o configure: catch Apple in more target triplets [6] - o configure: improve help string for some options [78] - o curl: add build options for safe/no CA bundle search (Windows) [26] - o curl_trc: fix build with verbose messages disabled [79] - o curl_url_set.md: document HOST handling when URL is parsed [2] - o CURLMOPT_PIPELINING.md: clarify that CURLPIPE_NOTHING is not default [54] - o docs/cmdline-opts: GnuTLS supports PKCS#11 URI in --cert option [101] - o ftp: fix 0-length last write on upload from stdin [76] - o lib, src, tests: added space around ternary expressions [56] - o lib/cw-out: initialize 'flush_all' directly [62] - o lib/src: white space edits to comply better with code style [47] - o lib: avoid assigning 'result' temporarily [97] - o lib: fix disabled-verbose-strings + enable-debug build warnings - o lib: fix unity builds with BearSSL, MSH3, Quiche, OmniOS [32] - o lib: use bool/TRUE/FALSE properly [48] - o libssh.c: handle EGAINS during proto-connect correctly [23] - o libssh2: use the Curl_* memory functions to avoid memdebug [22] - o multi.c: make stronger check for paused transfer before asserting [24] - o multi.c: warn/assert on stall only without timer [80] - o multi: avoid reading whole struct pointer from pointer [10] - o multi: make multi_handle_timeout use the connect timeout [98] - o negotiate: conditional check around GSS & SSL specific code [1] - o openssl: convert a memcpy to dynbuf use [57] - o openssl: remove two strcpy() calls [64] - o processhelp.pm: improve taskkill calls (Windows) [52] - o quic: use send/recvmmsg when available [93] - o request: on shutdown send, proceed normally on timeout [18] - o runtests.md: suggest a value for -j for torture tests - o runtests: drop unused code for old/classic-mingw support [87] - o select: use poll() if existing, avoid poll() with no sockets [75] - o sendf: add condition to max-filesize check [3] - o singleuse: make `git grep` faster, add Apple `nm` support [109] - o socks_gssapi: switch to dynbuf from buffer with strcpy [42] - o test1035: convert host name back to utf8 as should be [63] - o test1540: add debug logging [58] - o test190: replace %FTPTIME2 with a fixed value [34] - o test2502: add libtest debug tracing [60] - o test504: fix handling on pending connect [59] - o testrun: explicitly set proper IP address for stunnel listen/connect [61] - o tests/valgrind.pm: fix warnings with no valgrind report to show [25] - o tests: add and use `%PERL` variable to refer to the Perl binary [82] - o tests: add codeset-utf8 as a feature [66] - o tests: add file: tests with existing files [45] - o tests: check http/2 and http/3 server responsiveness [28] - o tests: delete duplicate macro check [53] - o tests: fix `%POSIX_PWD` on native Windows Perl [111] - o tests: fix keyword for test1411 - o tests: fix shell quoting on native Windows Perl [110] - o tests: fixup `checkcmd` `PATH` on non-unixy platforms [108] - o tests: improve mqtt server handling [27] - o tests: introduce %CLIENT6IP-NB [67] - o tests: let openssl generate random cert serials [91] - o tests: libtests and unit tests need explicit #include memdebug [7] - o tests: make precheck for HTTP on 127.0.0.1 into a feature [68] - o tests: Only log warnings or worse by default in smbserver [33] - o tests: postcheck is now in verify [69] - o tests: remove all valgrind disable instructions [21] - o tests: remove debug requirement on 38 tests [100] - o tests: remove the %FTPTIME3 variable [41] - o tests: replace `%PWD` with `%FILE_PWD` for `file://` [84] - o tests: replace `%PWD` with `%SSH_PWD` in SCP/SFTP tests [112] - o tests: replace hard-coded `/dev/null` with variable [81] - o tests: simplify `pathhelp.pm`, avoid using external tools [95] - o tests: speed up builds with single-binary test bundles [29] - o tests: testrunner fairness [39] - o tests: testrunner reliability improvements [55] - o tests: use '-4' where needed [17] - o tidy-up: rename CURL_WINDOWS_APP to CURL_WINDOWS_UWP [36] - o tool: support --show-headers AND --remote-header-name [103] - o tool_doswin: simplify; remove unused options and strncpy calls [65] - o unit1660: fix unreachable code warning in no-SSL builds [30] - o url: connection reuse on h3 connections [20] - o urlapi: drop unused header [51] - o vtls: convert Curl_pin_peer_pubkey to use dynbuf [74] - o vtls: convert pubkey_pem_to_der to use dynbuf [90] - o wolfssl: convert malloc + memcpys to dynbuf for cipher string [96] + o build: omit certain deps from `libcurl.pc` unless found via `pkg-config` [27] + o build: use `_fseeki64()` on Windows, drop detections [41] + o cmake: drop cmake args list from `buildinfo.txt` [8] + o cmake: restore cmake args list in `buildinfo.txt` [26] + o cmake: sync GSS config code with other deps [28] + o cmake: typo in comment + o cmake: work around `ios.toolchain.cmake` breaking feature-detections [37] + o cmakelint: fix to check root `CMakeLists.txt` [36] + o cmdline/ech.md: formatting cleanups [13] + o configure: add FIXMEs for disabled pkg-config references + o configure: do not echo most inherited `LDFLAGS` to config files [31] + o configure: replace `$#` shell syntax [25] + o cookie: treat cookie name case sensitively [4] + o curl-rustls.m4: keep existing `CPPFLAGS`/`LDFLAGS` when detected [40] + o curl.h: mark two error codes as obsolete [19] + o curl: --test-duphandle in debug builds runs "duphandled" [6] + o curl: rename struct var to fix AIX build [24] + o CURLOPT_PREREQFUNCTION.md: add result code on failure [23] + o duphandle: also init netrc [3] + o ECH: enable support for the AWS-LC backend [5] + o krb5: fix socket/sockindex confusion, MSVC compiler warnings [22] + o libssh: when using IPv6 numerical address, add brackets [43] + o macos: disable gcc `availability` workaround as needed [7] + o mbedtls: call psa_crypt_init() in global init [2] + o mk-ca-bundle: remove CKA_NSS_SERVER_DISTRUST_AFTER conditions [39] + o multi: add clarifying comment for wakeup_write() [9] + o netrc: address several netrc parser flaws [17] + o netrc: support large file, longer lines, longer tokens [14] + o nghttp2: use custom memory functions [1] + o os400: Fix IBMi builds [33] + o os400: Fix IBMi EBCDIC conversion of arguments [34] + o setopt: fix CURLOPT_HTTP_CONTENT_DECODING [15] + o socket: handle binding to "host!" [16] + o socketpair: fix enabling `USE_EVENTFD` [30] + o strtok: use namespaced `strtok_r` macro instead of redefining it [29] + o TODO: consider OCSP stapling by default [11] + o vtls: fix compile warning when ALPN is not available [12] This release includes the following known bugs: @@ -128,7 +59,6 @@ For all changes ever done in curl: Planned upcoming removals include: - o Hyper support after February 2025 [89] o TLS libraries not supporting TLS 1.3 See https://curl.se/dev/deprecate.html for details @@ -136,118 +66,49 @@ Planned upcoming removals include: This release would not have looked like this without help, code, reports and advice from friends like these: - Aki Sakurai, Baruch Siach, Dan Fandrich, Daniel Stenberg, - Denis Goleshchikhin, Deniz SÃļkmen, dependabot[bot], Gabriel Marin, - Ian Spence, Jon Rumsey, Kai Pastor, lomberd2 on github, MacKenzie, - Montg0mery on github, Nicolas George, Pavel Kropachev, ralfjunker on github, - Rasmus Melchior Jacobsen, Ray Satiro, renovate[bot], Stefan Eissing, - Tal Regev, Tatsuhiro Tsujikawa, Testclutch, Venkat Krishna R, Viktor Szakats, - vvb2060 on github - (27 contributors) + Andrew Ayer, Andrew Kirillov, andrewkirillov-ibm, Andy Fiddaman, Ben Greear, + Bo Anderson, Dan Fandrich, Daniel Engberg, Daniel Stenberg, Dan Rosser, + delogicsreal on github, Ethan Everett, Harmen Stoppels, Harry Sintonen, + henrikjehgmti on github, Jesus Malo Poyatos, Kai Pastor, Logan Buth, + Maarten Billemont, marcos-ng on github, Moritz, Nicolas F., Peter Marko, + Ray Satiro, renovate[bot], Samuel Henrique, Stefan Eissing, Tal Regev, + Thomas, tranzystorekk on github, Viktor Szakats, wxiaoguang on github + (32 contributors) References to bug reports and discussions on issues: - [1] = https://curl.se/bug/?i=14938 - [2] = https://curl.se/bug/?i=14942 - [3] = https://curl.se/bug/?i=14958 - [4] = https://curl.se/bug/?i=14941 - [5] = https://curl.se/bug/?i=14818 - [6] = https://curl.se/bug/?i=14728 - [7] = https://curl.se/bug/?i=15007 - [8] = https://curl.se/bug/?i=14824 - [9] = https://curl.se/bug/?i=14991 - [10] = https://curl.se/bug/?i=15063 - [11] = https://curl.se/bug/?i=14883 - [12] = https://curl.se/bug/?i=14789 - [13] = https://curl.se/bug/?i=14658 - [14] = https://curl.se/bug/?i=15003 - [15] = https://curl.se/bug/?i=14815 - [16] = https://curl.se/bug/?i=14906 - [17] = https://curl.se/bug/?i=15060 - [18] = https://curl.se/bug/?i=14843 - [19] = https://curl.se/bug/?i=14966 - [20] = https://curl.se/bug/?i=14890 - [21] = https://curl.se/bug/?i=14983 - [22] = https://curl.se/bug/?i=14984 - [23] = https://curl.se/bug/?i=14982 - [24] = https://curl.se/bug/?i=14981 - [25] = https://curl.se/bug/?i=14977 - [26] = https://curl.se/bug/?i=14582 - [27] = https://curl.se/bug/?i=15059 - [28] = https://curl.se/bug/?i=15058 - [29] = https://curl.se/bug/?i=14772 - [30] = https://curl.se/bug/?i=14971 - [31] = https://curl.se/bug/?i=14765 - [32] = https://curl.se/bug/?i=14932 - [33] = https://curl.se/bug/?i=14950 - [34] = https://curl.se/bug/?i=15056 - [35] = https://curl.se/bug/?i=14969 - [36] = https://curl.se/bug/?i=14881 - [37] = https://curl.se/bug/?i=14763 - [38] = https://curl.se/bug/?i=14690 - [39] = https://curl.se/bug/?i=14967 - [40] = https://github.com/curl/curl-for-win/blob/8b8909e1206de1dcca356a8dd33eb1e4ffeea7fd/curl.sh#L289 - [41] = https://curl.se/bug/?i=15064 - [42] = https://curl.se/bug/?i=15057 - [43] = https://curl.se/bug/?i=14909 - [44] = https://curl.se/bug/?i=14828 - [45] = https://curl.se/bug/?i=15045 - [46] = https://curl.se/bug/?i=14921 - [47] = https://curl.se/bug/?i=14921 - [48] = https://curl.se/bug/?i=15123 - [49] = https://curl.se/bug/?i=14924 - [50] = https://curl.se/bug/?i=14918 - [51] = https://curl.se/bug/?i=14867 - [52] = https://curl.se/bug/?i=14959 - [53] = https://curl.se/bug/?i=14963 - [54] = https://curl.se/bug/?i=14961 - [55] = https://curl.se/bug/?i=14960 - [56] = https://curl.se/bug/?i=14912 - [57] = https://curl.se/bug/?i=15049 - [58] = https://curl.se/bug/?i=15055 - [59] = https://curl.se/bug/?i=15054 - [60] = https://curl.se/bug/?i=15053 - [61] = https://curl.se/bug/?i=15051 - [62] = https://curl.se/bug/?i=15044 - [63] = https://curl.se/bug/?i=15050 - [64] = https://curl.se/bug/?i=15052 - [65] = https://curl.se/bug/?i=15047 - [66] = https://curl.se/bug/?i=15039 - [67] = https://curl.se/bug/?i=15039 - [68] = https://curl.se/bug/?i=15039 - [69] = https://curl.se/bug/?i=15046 - [70] = https://curl.se/bug/?i=15013 - [71] = https://curl.se/bug/?i=15034 - [72] = https://curl.se/bug/?i=15035 - [74] = https://curl.se/bug/?i=15126 - [75] = https://curl.se/bug/?i=15096 - [76] = https://curl.se/bug/?i=15101 - [77] = https://curl.se/bug/?i=14990 - [78] = https://curl.se/bug/?i=15033 - [79] = https://curl.se/bug/?i=15026 - [80] = https://curl.se/bug/?i=15028 - [81] = https://curl.se/bug/?i=15098 - [82] = https://curl.se/bug/?i=15097 - [83] = https://curl.se/bug/?i=15027 - [84] = https://curl.se/bug/?i=15090 - [85] = https://curl.se/bug/?i=15012 - [86] = https://curl.se/bug/?i=15088 - [87] = https://curl.se/bug/?i=15087 - [88] = https://curl.se/bug/?i=15001 - [89] = https://curl.se/bug/?i=15010 - [90] = https://curl.se/bug/?i=15126 - [91] = https://curl.se/bug/?i=15129 - [93] = https://curl.se/bug/?i=14880 - [95] = https://curl.se/bug/?i=15111 - [96] = https://curl.se/bug/?i=15124 - [97] = https://curl.se/bug/?i=15122 - [98] = https://curl.se/bug/?i=15100 - [100] = https://curl.se/bug/?i=15114 - [101] = https://curl.se/bug/?i=15075 - [103] = https://curl.se/bug/?i=15110 - [106] = https://curl.se/bug/?i=14936 - [108] = https://curl.se/bug/?i=15106 - [109] = https://curl.se/bug/?i=15070 - [110] = https://curl.se/bug/?i=15105 - [111] = https://curl.se/bug/?i=15104 - [112] = https://curl.se/bug/?i=15103 + [1] = https://curl.se/bug/?i=15527 + [2] = https://curl.se/bug/?i=15500 + [3] = https://curl.se/bug/?i=15496 + [4] = https://curl.se/bug/?i=15492 + [5] = https://curl.se/bug/?i=15499 + [6] = https://curl.se/bug/?i=15504 + [7] = https://curl.se/bug/?i=15508 + [8] = https://curl.se/bug/?i=15501 + [9] = https://curl.se/bug/?i=15600 + [11] = https://curl.se/bug/?i=15483 + [12] = https://curl.se/bug/?i=15515 + [13] = https://curl.se/bug/?i=15506 + [14] = https://curl.se/bug/?i=15513 + [15] = https://curl.se/bug/?i=15511 + [16] = https://curl.se/bug/?i=15553 + [17] = https://curl.se/bug/?i=15586 + [19] = https://curl.se/bug/?i=15538 + [22] = https://curl.se/bug/?i=15585 + [23] = https://curl.se/bug/?i=15542 + [24] = https://curl.se/bug/?i=15580 + [25] = https://curl.se/bug/?i=15584 + [26] = https://curl.se/bug/?i=15563 + [27] = https://curl.se/bug/?i=15469 + [28] = https://curl.se/bug/?i=15545 + [29] = https://curl.se/bug/?i=15549 + [30] = https://curl.se/bug/?i=15561 + [31] = https://curl.se/bug/?i=15533 + [33] = https://curl.se/bug/?i=15566 + [34] = https://curl.se/bug/?i=15570 + [36] = https://curl.se/bug/?i=15565 + [37] = https://curl.se/bug/?i=15557 + [39] = https://curl.se/bug/?i=15547 + [40] = https://curl.se/bug/?i=15546 + [41] = https://curl.se/bug/?i=15525 + [43] = https://curl.se/bug/?i=15522 diff --git a/REUSE.toml b/REUSE.toml index 431cbb59997c5c..e242452d8ff0e5 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -49,6 +49,7 @@ path = [ "lib/vtls/.checksrc", "src/.checksrc", "tests/libtest/.checksrc", + "tests/server/.checksrc", ] SPDX-FileCopyrightText = "Daniel Stenberg, , et al." SPDX-License-Identifier = "curl" diff --git a/acinclude.m4 b/acinclude.m4 index ef08969829fcfa..916130b393f704 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -504,7 +504,7 @@ AC_DEFUN([CURL_CHECK_LIBS_LDAP], [ '-llber -lldap' \ '-lldapssl -lldapx -lldapsdk' \ '-lldapsdk -lldapx -lldapssl' \ - '-lldap -llber -lssl -lcrypto' ; do + '-lldap -llber -lssl -lcrypto'; do if test "$curl_cv_ldap_LIBS" = "unknown"; then if test -z "$x_nlibs"; then @@ -565,6 +565,10 @@ AC_DEFUN([CURL_CHECK_LIBS_LDAP], [ else LIBS="$curl_cv_ldap_LIBS $curl_cv_save_LIBS" fi + # FIXME: Enable when ldap was detected via pkg-config + if false; then + LIBCURL_PC_REQUIRES_PRIVATE="ldap $LIBCURL_PC_REQUIRES_PRIVATE" + fi AC_MSG_RESULT([$curl_cv_ldap_LIBS]) ;; esac @@ -1551,10 +1555,14 @@ AC_DEFUN([CURL_PREPARE_BUILDINFO], [ *-*-*bsd*|*-*-aix*|*-*-hpux*|*-*-interix*|*-*-irix*|*-*-linux*|*-*-solaris*|*-*-sunos*|*-apple-*|*-*-cygwin*|*-*-msys*) curl_pflags="${curl_pflags} UNIX";; esac + case $host in + *-*-*bsd*) + curl_pflags="${curl_pflags} BSD";; + esac + fi + if test "$curl_cv_cygwin" = 'yes'; then + curl_pflags="${curl_pflags} CYGWIN" fi - case $host_os in - cygwin*|msys*) curl_pflags="${curl_pflags} CYGWIN";; - esac case $host_os in msys*) curl_pflags="${curl_pflags} MSYS";; esac diff --git a/appveyor.sh b/appveyor.sh index f9647f2817524f..46501437dcfba4 100644 --- a/appveyor.sh +++ b/appveyor.sh @@ -38,6 +38,7 @@ openssl_root="$(cygpath "${openssl_root_win}")" if [ "${BUILD_SYSTEM}" = 'CMake' ]; then options='' [[ "${TARGET:-}" = *'ARM64'* ]] && SKIP_RUN='ARM64 architecture' + [ -n "${TOOLSET:-}" ] && options+=" -T ${TOOLSET}" [ "${OPENSSL}" = 'ON' ] && options+=" -DOPENSSL_ROOT_DIR=${openssl_root_win}" [ -n "${CURLDEBUG:-}" ] && options+=" -DENABLE_CURLDEBUG=${CURLDEBUG}" [ "${PRJ_CFG}" = 'Debug' ] && options+=' -DCMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG=' @@ -62,6 +63,10 @@ if [ "${BUILD_SYSTEM}" = 'CMake' ]; then '-DCMAKE_INSTALL_PREFIX=C:/curl' \ "-DCMAKE_BUILD_TYPE=${PRJ_CFG}" \ '-DCURL_USE_LIBPSL=OFF' + if false; then + cat _bld/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true + fi + echo 'curl_config.h'; grep -F '#define' _bld/lib/curl_config.h | sort || true # shellcheck disable=SC2086 if ! cmake --build _bld --config "${PRJ_CFG}" --parallel 2 -- ${BUILD_OPT:-}; then if [ "${PRJ_GEN}" = 'Visual Studio 9 2008' ]; then @@ -118,10 +123,6 @@ else echo "Skip running curl.exe. Reason: ${SKIP_RUN}" fi -if false; then - cat CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true -fi - # build tests if [[ "${TFLAGS}" != 'skipall' ]] && \ diff --git a/appveyor.yml b/appveyor.yml index d96d3e59bb1aea..8209cd119bdaf1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,13 +35,15 @@ environment: OPENSSL: 'OFF' DEBUG: 'ON' SHARED: 'OFF' + HTTP_ONLY: 'OFF' TFLAGS: 'skiprun' EXAMPLES: 'OFF' + matrix: # generated CMake-based Visual Studio builds - - job_name: 'CMake, VS2008, Release, x86, Schannel, Build-tests' + - job_name: 'CMake, VS2008, Release, x86, Schannel, Shared, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' BUILD_SYSTEM: CMake PRJ_GEN: 'Visual Studio 9 2008' @@ -49,19 +51,17 @@ environment: DEBUG: 'OFF' SCHANNEL: 'ON' ENABLE_UNICODE: 'OFF' - HTTP_ONLY: 'OFF' SHARED: 'ON' - - job_name: 'CMake, VS2008, Debug, x86, Schannel, Build-tests & examples' + - job_name: 'CMake, VS2008, Debug, x86, Schannel, Shared, Build-tests & examples' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2015' BUILD_SYSTEM: CMake PRJ_GEN: 'Visual Studio 9 2008' PRJ_CFG: Debug SCHANNEL: 'ON' ENABLE_UNICODE: 'OFF' - HTTP_ONLY: 'OFF' SHARED: 'ON' EXAMPLES: 'ON' - - job_name: 'CMake, VS2022, Release, x64, OpenSSL 3.3, Build-tests' + - job_name: 'CMake, VS2022, Release, x64, OpenSSL 3.3, Shared, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' BUILD_SYSTEM: CMake PRJ_GEN: 'Visual Studio 17 2022' @@ -70,7 +70,6 @@ environment: OPENSSL: 'ON' SCHANNEL: 'OFF' ENABLE_UNICODE: 'OFF' - HTTP_ONLY: 'OFF' SHARED: 'ON' - job_name: 'CMake, VS2022, Release, arm64, Schannel, Static, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' @@ -80,7 +79,6 @@ environment: PRJ_CFG: Release SCHANNEL: 'ON' ENABLE_UNICODE: 'OFF' - HTTP_ONLY: 'OFF' DEBUG: 'OFF' CURLDEBUG: 'ON' - job_name: 'CMake, VS2010, Debug, x64, Schannel, Static, Build-tests & examples' @@ -90,8 +88,17 @@ environment: PRJ_CFG: Debug SCHANNEL: 'ON' ENABLE_UNICODE: 'OFF' - HTTP_ONLY: 'OFF' EXAMPLES: 'ON' + - job_name: 'CMake, VS2022, Debug, x64, Schannel, Static, Unicode, Build-tests & examples, clang-cl' + APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' + BUILD_SYSTEM: CMake + PRJ_GEN: 'Visual Studio 17 2022' + TARGET: '-A x64' + PRJ_CFG: Debug + SCHANNEL: 'ON' + ENABLE_UNICODE: 'ON' + EXAMPLES: 'ON' + TOOLSET: 'ClangCl' - job_name: 'CMake, VS2022, Debug, x64, Schannel, Static, Unicode, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' BUILD_SYSTEM: CMake @@ -100,8 +107,7 @@ environment: PRJ_CFG: Debug SCHANNEL: 'ON' ENABLE_UNICODE: 'ON' - HTTP_ONLY: 'OFF' - - job_name: 'CMake, VS2022, Release, x64, Schannel, Shared, Unicode, DEBUGBULID, no-CURLDEBUG, Build-tests' + - job_name: 'CMake, VS2022, Release, x64, Schannel, Shared, Unicode, DEBUGBUILD, no-CURLDEBUG, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' BUILD_SYSTEM: CMake PRJ_GEN: 'Visual Studio 17 2022' @@ -109,7 +115,6 @@ environment: PRJ_CFG: Release SCHANNEL: 'ON' ENABLE_UNICODE: 'ON' - HTTP_ONLY: 'OFF' SHARED: 'ON' CURLDEBUG: 'OFF' - job_name: 'CMake, VS2022, Debug, x64, no SSL, Static, Build-tests' @@ -120,7 +125,6 @@ environment: PRJ_CFG: Debug SCHANNEL: 'OFF' ENABLE_UNICODE: 'OFF' - HTTP_ONLY: 'OFF' - job_name: 'CMake, VS2022, Debug, x64, no SSL, Static, HTTP only, Build-tests' APPVEYOR_BUILD_WORKER_IMAGE: 'Visual Studio 2022' BUILD_SYSTEM: CMake diff --git a/configure.ac b/configure.ac index e5998a10628344..8026ae83c50cb2 100644 --- a/configure.ac +++ b/configure.ac @@ -436,7 +436,7 @@ dnl AC_CANONICAL_HOST dnl Get system canonical name -AC_DEFINE_UNQUOTED(OS, "${host}", [cpu-machine-OS]) +AC_DEFINE_UNQUOTED(CURL_OS, "${host}", [cpu-machine-OS]) # Silence warning: ar: 'u' modifier ignored since 'D' is the default AC_SUBST(AR_FLAGS, [cr]) @@ -605,6 +605,11 @@ case $host in ;; esac +curl_cv_cygwin='no' +case $host_os in + cygwin*|msys*) curl_cv_cygwin='yes';; +esac + AM_CONDITIONAL([HAVE_WINDRES], [test "$curl_cv_native_windows" = "yes" && test -n "${RC}"]) @@ -1350,10 +1355,6 @@ if test "$HAVE_GETHOSTBYNAME" != "1"; then ) fi -if test "$HAVE_GETHOSTBYNAME" != "1"; then - AC_MSG_ERROR([couldn't find libraries for gethostbyname()]) -fi - CURL_CHECK_LIBS_CONNECT CURL_NETWORK_LIBS=$LIBS @@ -1512,6 +1513,7 @@ AS_HELP_STRING([--without-brotli], [disable BROTLI]), if test X"$OPT_BROTLI" != Xno; then dnl backup the pre-brotli variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -1547,6 +1549,7 @@ if test X"$OPT_BROTLI" != Xno; then fi LDFLAGS="$LDFLAGS $LD_BROTLI" + LDFLAGSPC="$LDFLAGSPC $LD_BROTLI" CPPFLAGS="$CPPFLAGS $CPP_BROTLI" LIBS="$LIB_BROTLI $LIBS" @@ -1580,6 +1583,7 @@ if test X"$OPT_BROTLI" != Xno; then else dnl no brotli, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS fi @@ -1599,6 +1603,7 @@ AS_HELP_STRING([--without-zstd], [disable libzstd]), if test X"$OPT_ZSTD" != Xno; then dnl backup the pre-zstd variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -1634,6 +1639,7 @@ if test X"$OPT_ZSTD" != Xno; then fi LDFLAGS="$LDFLAGS $LD_ZSTD" + LDFLAGSPC="$LDFLAGSPC $LD_ZSTD" CPPFLAGS="$CPPFLAGS $CPP_ZSTD" LIBS="$LIB_ZSTD $LIBS" @@ -1668,6 +1674,7 @@ if test X"$OPT_ZSTD" != Xno; then else dnl no zstd, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS fi @@ -2008,11 +2015,14 @@ if test x"$want_gss" = xyes; then AC_DEFINE(HAVE_GSSAPI, 1, [if you have GSS-API libraries]) HAVE_GSSAPI=1 curl_gss_msg="enabled (MIT Kerberos/Heimdal)" + link_pkgconfig='' if test -n "$gnu_gss"; then curl_gss_msg="enabled (GNU GSS)" LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" + LDFLAGSPC="$LDFLAGSPC $GSSAPI_LIB_DIR" LIBS="-lgss $LIBS" + link_pkgconfig=1 elif test -z "$GSSAPI_LIB_DIR"; then case $host in *-apple-*) @@ -2032,11 +2042,13 @@ if test x"$want_gss" = xyes; then elif test "$PKGCONFIG" != "no"; then gss_libs=`$PKGCONFIG --libs mit-krb5-gssapi` LIBS="$gss_libs $LIBS" + link_pkgconfig=1 elif test -f "$KRB5CONFIG"; then dnl krb5-config doesn't have --libs-only-L or similar, put everything dnl into LIBS gss_libs=`$KRB5CONFIG --libs gssapi` LIBS="$gss_libs $LIBS" + link_pkgconfig=1 else case $host in *-hp-hpux*) @@ -2049,6 +2061,7 @@ if test x"$want_gss" = xyes; then if test "$GSSAPI_ROOT" != "yes"; then LDFLAGS="$LDFLAGS -L$GSSAPI_ROOT/lib$libsuff" + LDFLAGSPC="$LDFLAGSPC -L$GSSAPI_ROOT/lib$libsuff" LIBS="-l$gss_libname $LIBS" else LIBS="-l$gss_libname $LIBS" @@ -2058,6 +2071,7 @@ if test x"$want_gss" = xyes; then esac else LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR" + LDFLAGSPC="$LDFLAGSPC $GSSAPI_LIB_DIR" case $host in *-hp-hpux*) LIBS="-lgss $LIBS" @@ -2067,6 +2081,15 @@ if test x"$want_gss" = xyes; then ;; esac fi + if test -n "$link_pkgconfig"; then + if test -n "$gnu_gss"; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE gss" + elif test "x$not_mit" = "x1"; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE heimdal-gssapi" + else + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mit-krb5-gssapi" + fi + fi else CPPFLAGS="$save_CPPFLAGS" fi @@ -2249,6 +2272,7 @@ AS_HELP_STRING([--without-libpsl], [disable LIBPSL]), if test X"$OPT_LIBPSL" != Xno; then dnl backup the pre-libpsl variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -2281,6 +2305,7 @@ if test X"$OPT_LIBPSL" != Xno; then fi LDFLAGS="$LDFLAGS $LD_PSL" + LDFLAGSPC="$LDFLAGSPC $LD_PSL" CPPFLAGS="$CPPFLAGS $CPP_PSL" LIBS="$LIB_PSL $LIBS" @@ -2296,6 +2321,7 @@ if test X"$OPT_LIBPSL" != Xno; then ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -2359,6 +2385,7 @@ AS_HELP_STRING([--with-wolfssh], [enable wolfssh]), if test X"$OPT_LIBSSH2" != Xno; then dnl backup the pre-libssh2 variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -2394,6 +2421,7 @@ if test X"$OPT_LIBSSH2" != Xno; then fi LDFLAGS="$LDFLAGS $LD_SSH2" + LDFLAGSPC="$LDFLAGSPC $LD_SSH2" CPPFLAGS="$CPPFLAGS $CPP_SSH2" LIBS="$LIB_SSH2 $LIBS" @@ -2428,12 +2456,14 @@ if test X"$OPT_LIBSSH2" != Xno; then else dnl no libssh2, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS fi elif test X"$OPT_LIBSSH" != Xno; then dnl backup the pre-libssh variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -2469,6 +2499,7 @@ elif test X"$OPT_LIBSSH" != Xno; then fi LDFLAGS="$LDFLAGS $LD_SSH" + LDFLAGSPC="$LDFLAGSPC $LD_SSH" CPPFLAGS="$CPPFLAGS $CPP_SSH" LIBS="$LIB_SSH $LIBS" @@ -2506,18 +2537,22 @@ elif test X"$OPT_LIBSSH" != Xno; then else dnl no libssh, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS fi elif test X"$OPT_WOLFSSH" != Xno; then dnl backup the pre-wolfssh variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" if test "$OPT_WOLFSSH" != yes; then WOLFCONFIG="$OPT_WOLFSSH/bin/wolfssh-config" - LDFLAGS="$LDFLAGS `$WOLFCONFIG --libs`" + WOLFSSH_LIBS=`$WOLFCONFIG --libs` + LDFLAGS="$LDFLAGS $WOLFSSH_LIBS" + LDFLAGSPC="$LDFLAGSPC $WOLFSSH_LIBS" CPPFLAGS="$CPPFLAGS `$WOLFCONFIG --cflags`" fi @@ -2545,6 +2580,7 @@ AS_HELP_STRING([--without-librtmp], [disable LIBRTMP]), if test X"$OPT_LIBRTMP" != Xno; then dnl backup the pre-librtmp variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -2585,6 +2621,7 @@ if test X"$OPT_LIBRTMP" != Xno; then fi LDFLAGS="$LDFLAGS $LD_RTMP" + LDFLAGSPC="$LDFLAGSPC $LD_RTMP" CPPFLAGS="$CPPFLAGS $CPP_RTMP" LIBS="$LIB_RTMP $LIBS" @@ -2600,6 +2637,7 @@ if test X"$OPT_LIBRTMP" != Xno; then ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -2728,6 +2766,7 @@ AS_HELP_STRING([--without-winidn], [disable Windows native IDN]), dnl WinIDN library support has been requested clean_CPPFLAGS="$CPPFLAGS" clean_LDFLAGS="$LDFLAGS" + clean_LDFLAGSPC="$LDFLAGSPC" clean_LIBS="$LIBS" WINIDN_LIBS="-lnormaliz" WINIDN_CPPFLAGS="" @@ -2741,6 +2780,7 @@ AS_HELP_STRING([--without-winidn], [disable Windows native IDN]), # CPPFLAGS="$CPPFLAGS $WINIDN_CPPFLAGS" LDFLAGS="$LDFLAGS $WINIDN_LDFLAGS" + LDFLAGSPC="$LDFLAGSPC $WINIDN_LDFLAGS" LIBS="$WINIDN_LIBS $LIBS" # AC_MSG_CHECKING([if IdnToUnicode can be linked]) @@ -2774,6 +2814,7 @@ AS_HELP_STRING([--without-winidn], [disable Windows native IDN]), AC_MSG_WARN([Cannot find libraries for IDN support: IDN disabled]) CPPFLAGS="$clean_CPPFLAGS" LDFLAGS="$clean_LDFLAGS" + LDFLAGSPC="$clean_LDFLAGSPC" LIBS="$clean_LIBS" fi fi @@ -2863,6 +2904,7 @@ if test "$want_idn" = "yes"; then dnl idn library support has been requested clean_CPPFLAGS="$CPPFLAGS" clean_LDFLAGS="$LDFLAGS" + clean_LDFLAGSPC="$LDFLAGSPC" clean_LIBS="$LIBS" PKGCONFIG="no" # @@ -2913,6 +2955,7 @@ if test "$want_idn" = "yes"; then # CPPFLAGS="$CPPFLAGS $IDN_CPPFLAGS" LDFLAGS="$LDFLAGS $IDN_LDFLAGS" + LDFLAGSPC="$LDFLAGSPC $IDN_LDFLAGS" LIBS="$IDN_LIBS $LIBS" # AC_MSG_CHECKING([if idn2_lookup_ul can be linked]) @@ -2944,6 +2987,7 @@ if test "$want_idn" = "yes"; then AC_MSG_WARN([Cannot find libidn2]) CPPFLAGS="$clean_CPPFLAGS" LDFLAGS="$clean_LDFLAGS" + LDFLAGSPC="$clean_LDFLAGSPC" LIBS="$clean_LIBS" want_idn="no" fi @@ -2986,6 +3030,7 @@ esac if test X"$want_nghttp2" != Xno; then dnl backup the pre-nghttp2 variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3019,6 +3064,7 @@ if test X"$want_nghttp2" != Xno; then fi LDFLAGS="$LDFLAGS $LD_H2" + LDFLAGSPC="$LDFLAGSPC $LD_H2" CPPFLAGS="$CPPFLAGS $CPP_H2" LIBS="$LIB_H2 $LIBS" @@ -3040,6 +3086,7 @@ if test X"$want_nghttp2" != Xno; then ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3086,6 +3133,7 @@ if test X"$want_tcp2" != Xno; then dnl backup the pre-ngtcp2 variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3105,6 +3153,7 @@ if test X"$want_tcp2" != Xno; then AC_MSG_NOTICE([-L is $LD_TCP2]) LDFLAGS="$LDFLAGS $LD_TCP2" + LDFLAGSPC="$LDFLAGSPC $LD_TCP2" CPPFLAGS="$CPPFLAGS $CPP_TCP2" LIBS="$LIB_TCP2 $LIBS" @@ -3125,6 +3174,7 @@ if test X"$want_tcp2" != Xno; then ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3142,6 +3192,7 @@ fi if test "x$NGTCP2_ENABLED" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BORINGSSL" != "x1"; then dnl backup the pre-ngtcp2_crypto_quictls variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3161,6 +3212,7 @@ if test "x$NGTCP2_ENABLED" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_QUICTLS]) LDFLAGS="$LDFLAGS $LD_NGTCP2_CRYPTO_QUICTLS" + LDFLAGSPC="$LDFLAGSPC $LD_NGTCP2_CRYPTO_QUICTLS" CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_QUICTLS" LIBS="$LIB_NGTCP2_CRYPTO_QUICTLS $LIBS" @@ -3181,6 +3233,7 @@ if test "x$NGTCP2_ENABLED" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3198,6 +3251,7 @@ fi if test "x$NGTCP2_ENABLED" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BORINGSSL" = "x1"; then dnl backup the pre-ngtcp2_crypto_boringssl variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3217,6 +3271,7 @@ if test "x$NGTCP2_ENABLED" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_BORINGSSL]) LDFLAGS="$LDFLAGS $LD_NGTCP2_CRYPTO_BORINGSSL" + LDFLAGSPC="$LDFLAGSPC $LD_NGTCP2_CRYPTO_BORINGSSL" CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_BORINGSSL" LIBS="$LIB_NGTCP2_CRYPTO_BORINGSSL $LIBS" @@ -3237,6 +3292,7 @@ if test "x$NGTCP2_ENABLED" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3254,6 +3310,7 @@ fi if test "x$NGTCP2_ENABLED" = "x1" -a "x$GNUTLS_ENABLED" = "x1"; then dnl backup the pre-ngtcp2_crypto_gnutls variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3273,6 +3330,7 @@ if test "x$NGTCP2_ENABLED" = "x1" -a "x$GNUTLS_ENABLED" = "x1"; then AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_GNUTLS]) LDFLAGS="$LDFLAGS $LD_NGTCP2_CRYPTO_GNUTLS" + LDFLAGSPC="$LDFLAGSPC $LD_NGTCP2_CRYPTO_GNUTLS" CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_GNUTLS" LIBS="$LIB_NGTCP2_CRYPTO_GNUTLS $LIBS" @@ -3293,6 +3351,7 @@ if test "x$NGTCP2_ENABLED" = "x1" -a "x$GNUTLS_ENABLED" = "x1"; then ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3310,6 +3369,7 @@ fi if test "x$NGTCP2_ENABLED" = "x1" -a "x$WOLFSSL_ENABLED" = "x1"; then dnl backup the pre-ngtcp2_crypto_wolfssl variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3329,6 +3389,7 @@ if test "x$NGTCP2_ENABLED" = "x1" -a "x$WOLFSSL_ENABLED" = "x1"; then AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_WOLFSSL]) LDFLAGS="$LDFLAGS $LD_NGTCP2_CRYPTO_WOLFSSL" + LDFLAGSPC="$LDFLAGSPC $LD_NGTCP2_CRYPTO_WOLFSSL" CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_WOLFSSL" LIBS="$LIB_NGTCP2_CRYPTO_WOLFSSL $LIBS" @@ -3349,6 +3410,7 @@ if test "x$NGTCP2_ENABLED" = "x1" -a "x$WOLFSSL_ENABLED" = "x1"; then ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3445,6 +3507,7 @@ if test X"$want_nghttp3" != Xno; then dnl backup the pre-nghttp3 variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3464,6 +3527,7 @@ if test X"$want_nghttp3" != Xno; then AC_MSG_NOTICE([-L is $LD_NGHTTP3]) LDFLAGS="$LDFLAGS $LD_NGHTTP3" + LDFLAGSPC="$LDFLAGSPC $LD_NGHTTP3" CPPFLAGS="$CPPFLAGS $CPP_NGHTTP3" LIBS="$LIB_NGHTTP3 $LIBS" @@ -3483,6 +3547,7 @@ if test X"$want_nghttp3" != Xno; then ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3564,6 +3629,7 @@ if test X"$want_quiche" != Xno; then dnl backup the pre-quiche variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3583,6 +3649,7 @@ if test X"$want_quiche" != Xno; then AC_MSG_NOTICE([-L is $LD_QUICHE]) LDFLAGS="$LDFLAGS $LD_QUICHE" + LDFLAGSPC="$LDFLAGSPC $LD_QUICHE" CPPFLAGS="$CPPFLAGS $CPP_QUICHE" LIBS="$LIB_QUICHE $LIBS" @@ -3676,6 +3743,7 @@ if test X"$want_msh3" != Xno; then dnl backup the pre-msh3 variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3684,6 +3752,7 @@ if test X"$want_msh3" != Xno; then CPP_MSH3="-I$want_msh3_path/include" DIR_MSH3="$want_msh3_path/lib" LDFLAGS="$LDFLAGS $LD_MSH3" + LDFLAGSPC="$LDFLAGSPC $LD_MSH3" CPPFLAGS="$CPPFLAGS $CPP_MSH3" fi LIBS="-lmsh3 $LIBS" @@ -3698,12 +3767,16 @@ if test X"$want_msh3" != Xno; then CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_MSH3" export CURL_LIBRARY_PATH AC_MSG_NOTICE([Added $DIR_MSH3 to CURL_LIBRARY_PATH]) - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libmsh3" + dnl FIXME: Enable when msh3 was detected via pkg-config + if false; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libmsh3" + fi experimental="$experimental HTTP3" ) ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3742,6 +3815,7 @@ if test X"$want_libuv" != Xno; then dnl backup the pre-libuv variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -3761,6 +3835,7 @@ if test X"$want_libuv" != Xno; then AC_MSG_NOTICE([-L is $LD_LIBUV]) LDFLAGS="$LDFLAGS $LD_LIBUV" + LDFLAGSPC="$LDFLAGSPC $LD_LIBUV" CPPFLAGS="$CPPFLAGS $CPP_LIBUV" LIBS="$LIB_LIBUV $LIBS" @@ -3781,6 +3856,7 @@ if test X"$want_libuv" != Xno; then ], dnl not found, revert back to clean variables LDFLAGS=$CLEANLDFLAGS + LDFLAGSPC=$CLEANLDFLAGSPC CPPFLAGS=$CLEANCPPFLAGS LIBS=$CLEANLIBS ) @@ -3884,7 +3960,6 @@ AC_CHECK_HEADERS( sys/utime.h \ sys/poll.h \ poll.h \ - socket.h \ sys/resource.h \ libgen.h \ locale.h \ @@ -3917,10 +3992,10 @@ dnl default includes #include #endif #ifdef HAVE_NETINET_IN6_H -#include +#include /* is this really required to detect other headers? */ #endif #ifdef HAVE_SYS_UN_H -#include +#include /* is this really required to detect other headers? */ #endif ] ) @@ -4058,7 +4133,6 @@ CURL_CHECK_FUNC_GETHOSTBYNAME_R CURL_CHECK_FUNC_GETHOSTNAME CURL_CHECK_FUNC_GETPEERNAME CURL_CHECK_FUNC_GETSOCKNAME -CURL_CHECK_FUNC_IF_NAMETOINDEX CURL_CHECK_FUNC_GETIFADDRS CURL_CHECK_FUNC_GMTIME_R CURL_CHECK_FUNC_INET_NTOP @@ -4067,7 +4141,6 @@ CURL_CHECK_FUNC_IOCTL CURL_CHECK_FUNC_IOCTLSOCKET CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL CURL_CHECK_FUNC_MEMRCHR -CURL_CHECK_FUNC_POLL CURL_CHECK_FUNC_SIGACTION CURL_CHECK_FUNC_SIGINTERRUPT CURL_CHECK_FUNC_SIGNAL @@ -4095,7 +4168,6 @@ AC_CHECK_DECLS([getpwuid_r], [], [AC_DEFINE(HAVE_DECL_GETPWUID_R_MISSING, 1, "Se #include ]]) AC_CHECK_FUNCS([\ - _fseeki64 \ eventfd \ fnmatch \ geteuid \ @@ -4120,6 +4192,10 @@ AC_CHECK_FUNCS([\ utimes \ ]) +if test "$curl_cv_native_windows" = 'yes' -o "$curl_cv_cygwin" = 'yes'; then + AC_CHECK_FUNCS([_setmode]) +fi + if test -z "$ssl_backends"; then AC_CHECK_FUNCS([arc4random]) fi @@ -5005,8 +5081,27 @@ CURL_CONFIGURE_SYMBOL_HIDING dnl dnl All the library dependencies put into $LIB apply to libcurl only. dnl +LIBCURL_PC_LDFLAGS_PRIVATE='' +dnl Do not quote $INITIAL_LDFLAGS +set -- $INITIAL_LDFLAGS +while test -n "$1"; do + case "$1" in + -L* | --library-path=* | -F*) + LIBCURL_PC_LDFLAGS_PRIVATE="$LIBCURL_PC_LDFLAGS_PRIVATE $1" + ;; + -framework) + if test -n "$2"; then + LIBCURL_PC_LDFLAGS_PRIVATE="$LIBCURL_PC_LDFLAGS_PRIVATE $1 $2" + shift + fi + ;; + esac + shift +done +LIBCURL_PC_LDFLAGS_PRIVATE="$LIBCURL_PC_LDFLAGS_PRIVATE $LDFLAGSPC" LIBCURL_PC_LIBS_PRIVATE="$LIBS$PTHREAD" +AC_SUBST(LIBCURL_PC_LDFLAGS_PRIVATE) AC_SUBST(LIBCURL_PC_LIBS_PRIVATE) AC_SUBST(CURL_NETWORK_LIBS) AC_SUBST(CURL_NETWORK_AND_TIME_LIBS) @@ -5162,7 +5257,7 @@ if test "x$CURL_DISABLE_HTTP" != "x1"; then -o "x$MBEDTLS_ENABLED" = "x1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy" AC_MSG_RESULT([yes]) - elif test "x$WOLFSSL_ENABLED" = "x1" -a "x$WOLFSSL_FULL_BIO" = "x1"; then + elif test "x$WOLFSSL_ENABLED" = "x1" -a "x$WOLFSSL_BIO" = "x1"; then SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy" AC_MSG_RESULT([yes]) else @@ -5344,6 +5439,7 @@ squeeze DEFS squeeze LDFLAGS squeeze LIBS +squeeze LIBCURL_PC_LDFLAGS_PRIVATE squeeze LIBCURL_PC_LIBS_PRIVATE squeeze CURL_NETWORK_LIBS squeeze CURL_NETWORK_AND_TIME_LIBS @@ -5406,6 +5502,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl: CFLAGS extras: ${CURL_CFLAG_EXTRAS} CPPFLAGS: ${CPPFLAGS} LDFLAGS: ${LDFLAGS} + curl-config: ${LIBCURL_PC_LDFLAGS_PRIVATE} LIBS: ${LIBS} curl version: ${CURLVERSION} diff --git a/curl-config.in b/curl-config.in index 2dc40edcdcf667..e89c256392efd7 100644 --- a/curl-config.in +++ b/curl-config.in @@ -173,7 +173,7 @@ while test "$#" -gt 0; do --static-libs) if test 'X@ENABLE_STATIC@' != 'Xno'; then - echo "@libdir@/libcurl.@libext@ @LDFLAGS@ @LIBCURL_PC_LIBS_PRIVATE@" + echo "@libdir@/libcurl.@libext@ @LIBCURL_PC_LDFLAGS_PRIVATE@ @LIBCURL_PC_LIBS_PRIVATE@" else echo 'curl was built with static libraries disabled' >&2 exit 1 diff --git a/docs/CIPHERS.md b/docs/CIPHERS.md index 0807423d214c73..6e899e52dfad37 100644 --- a/docs/CIPHERS.md +++ b/docs/CIPHERS.md @@ -59,7 +59,7 @@ TLS_AES_128_CCM_8_SHA256 In addition to above list the following cipher suites can be used: `TLS_SM4_GCM_SM3` `TLS_SM4_CCM_SM3` `TLS_SHA256_SHA256` `TLS_SHA384_SHA384`. Usage of these cipher suites is not recommended. (The last two cipher suites -are NULL ciphers!) +are NULL ciphers, offering no encryption whatsoever.) ### Schannel notes diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index b361d88afa19d3..8a5c87f957e6b2 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -31,7 +31,7 @@ endif() if(BUILD_MISC_DOCS) foreach(_man_misc IN ITEMS "curl-config" "mk-ca-bundle") - set(_man_target "${CURL_BINARY_DIR}/docs/${_man_misc}.1") + set(_man_target "${CMAKE_CURRENT_BINARY_DIR}/${_man_misc}.1") add_custom_command(OUTPUT "${_man_target}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/cd2nroff" "${_man_misc}.md" > "${_man_target}" diff --git a/docs/CURL-DISABLE.md b/docs/CURL-DISABLE.md index 112b251cd5e841..63de4026a6d21d 100644 --- a/docs/CURL-DISABLE.md +++ b/docs/CURL-DISABLE.md @@ -40,7 +40,7 @@ Disable support for the negotiate authentication methods. ## `CURL_DISABLE_AWS` -Disable **AWS-SIG4** support. +Disable **aws-sigv4** support. ## `CURL_DISABLE_CA_SEARCH` diff --git a/docs/DISTROS.md b/docs/DISTROS.md index c3ae64c8148b58..93126afee0731b 100644 --- a/docs/DISTROS.md +++ b/docs/DISTROS.md @@ -144,7 +144,7 @@ Issues and patches for this are managed in the main curl project. - curl: https://formulae.brew.sh/formula/curl Homebrew's policy is that all patches and issues should be submitted upstream -unless it is very specific to Homebrew's way of packaging software. +unless it is specific to Homebrew's way of packaging software. ## MacPorts diff --git a/docs/ECH.md b/docs/ECH.md index 47b81c8e1e6bea..cf15314bd84be8 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 or BoringSSL as the -TLS provider. +from the command line. This works with OpenSSL, wolfSSL, BoringSSL or AWS-LC as +the TLS provider. This feature is EXPERIMENTAL. DO NOT USE IN PRODUCTION. @@ -94,7 +94,7 @@ We currently support the following new curl command line arguments/options: - ``grease`` if attempting ECH is not possible, then send a GREASE ECH extension - ``hard`` hard-fail the connection if ECH cannot be attempted - ``ecl:`` a base64 encoded ECHConfigList, rather than one accessed from the DNS - - ``pn:`` over-ride the ``public_name`` from an ECHConfigList + - ``pn:`` override the ``public_name`` from an ECHConfigList Note that in the above "attempt ECH" means the client emitting a TLS ClientHello with a "real" ECH extension, but that does not mean that the @@ -149,7 +149,7 @@ the verbose output, e.g.: ``` At that point, you could copy the base64 encoded value above and try again. -For now, this only works for the OpenSSL and BoringSSL builds. +For now, this only works for the OpenSSL and BoringSSL/AWS-LC builds. ## Default settings @@ -330,16 +330,16 @@ Then: autoreconf -fi LDFLAGS="-Wl,-rpath,$HOME/code/boringssl/inst/lib" ./configure --with-ssl=$HOME/code/boringssl/inst --enable-ech --enable-httpsrr ...lots of output... - WARNING: ECH HTTPSRR enabled but marked EXPERIMENTAL. Use with caution! + WARNING: ECH HTTPSRR enabled but marked EXPERIMENTAL. Use with caution. make ``` -The BoringSSL APIs are fairly similar to those in our ECH-enabled OpenSSL -fork, so code changes are also in ``lib/vtls/openssl.c``, protected +The BoringSSL/AWS-LC APIs are fairly similar to those in our ECH-enabled +OpenSSL fork, so code changes are also in ``lib/vtls/openssl.c``, protected via ``#ifdef OPENSSL_IS_BORINGSSL`` and are mostly obvious API variations. -The BoringSSL APIs however do not support the ``--ech pn:`` command line -variant as of now. +The BoringSSL/AWS-LC APIs however do not support the ``--ech pn:`` command +line variant as of now. ## wolfSSL build @@ -401,7 +401,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. (Both are reasonable.) +BoringSSL/AWS-LC. (Both are reasonable.) ## Additional notes @@ -474,5 +474,5 @@ to get the HTTPS RR and pass the ECHConfigList from that on the command line, if needed, or one can access the value from command line output in verbose more and then reuse that in another invocation. -Both our OpenSSL fork and BoringSSL have APIs for both controlling GREASE and -accessing and logging ``retry_configs``, it seems wolfSSL has neither. +Both our OpenSSL fork and BoringSSL/AWS-LC have APIs for both controlling GREASE +and accessing and logging ``retry_configs``, it seems wolfSSL has neither. diff --git a/docs/HTTP3.md b/docs/HTTP3.md index a7025f17343deb..3ff73c8e5b7d49 100644 --- a/docs/HTTP3.md +++ b/docs/HTTP3.md @@ -245,9 +245,9 @@ You can build curl with cmake: % cd .. % git clone https://github.com/curl/curl % cd curl - % cmake . -B build -DCURL_USE_OPENSSL=ON -DUSE_OPENSSL_QUIC=ON - % cmake --build build - % cmake --install build + % cmake . -B bld -DCURL_USE_OPENSSL=ON -DUSE_OPENSSL_QUIC=ON + % cmake --build bld + % cmake --install bld If `make install` results in `Permission denied` error, you need to prepend it with `sudo`. @@ -404,7 +404,7 @@ Get, build and install nghttp2: % git clone https://github.com/nghttp2/nghttp2.git % cd nghttp2 % autoreconf -fi - % PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/home/daniel/build-quictls/lib/pkgconfig:/home/daniel/build-nghttp3/lib/pkgconfig:/home/daniel/build-ngtcp2/lib/pkgconfig LDFLAGS=-L/home/daniel/build-quictls/lib CFLAGS=-I/home/daniel/build-quictls/include ./configure --enable-maintainer-mode --prefix=/home/daniel/build-nghttp2 --disable-shared --enable-app --enable-http3 --without-jemalloc --without-libxml2 --without-systemd + % PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/home/daniel/build-quictls/lib/pkgconfig:/home/daniel/build-nghttp3/lib/pkgconfig:/home/daniel/build-ngtcp2/lib/pkgconfig LDFLAGS=-L/home/daniel/build-quictls/lib CFLAGS=-I/home/daniel/build-quictls/include ./configure --enable-maintainer-mode --prefix=/home/daniel/build-nghttp2 --disable-shared --enable-app --enable-http3 --without-jemalloc --without-libxml2 --without-systemd % make && make install Run the local h3 server on port 9443, make it proxy all traffic through to diff --git a/docs/INSTALL-CMAKE.md b/docs/INSTALL-CMAKE.md index 0457efe2dcef35..a74e1930eadd0e 100644 --- a/docs/INSTALL-CMAKE.md +++ b/docs/INSTALL-CMAKE.md @@ -16,23 +16,6 @@ instructions below for the platform you are building on. CMake builds can be configured either from the command line, or from one of CMake's GUIs. -# Current flaws in the curl CMake build - -Missing features in the CMake build: - - - Builds libcurl without large file support - - Does not support all SSL libraries (only OpenSSL, Schannel, Secure - Transport, and mbedTLS, wolfSSL) - - Does not allow different resolver backends (no c-ares build support) - - No RTMP support built - - Does not allow build curl and libcurl debug enabled - - Does not allow a custom CA bundle path - - Does not allow you to disable specific protocols from the build - - Does not find or use krb4 or GSS - - Rebuilds test files too eagerly, but still cannot run the tests - - Does not detect the correct `strerror_r` flavor when cross-compiling - (issue #1123) - # Configuring A CMake configuration of curl is similar to the autotools build of curl. @@ -107,6 +90,25 @@ Build (you have to specify the build directory). $ cmake --build ../curl-build +## Static builds + +The CMake build setup is primarily done to work with shared/dynamic third +party dependencies. When linking with shared libraries, the dependency "chain" +is handled automatically by the library loader - on all modern systems. + +If you instead link with a static library, you need to provide all the +dependency libraries already at the link command line. + +Figuring out all the dependency libraries for a given library is hard, as it +might involve figuring out the dependencies of the dependencies and they vary +between platforms and can change between versions. + +When using static dependencies, the build scripts mostly assume that you, the +user, provide all the necessary additional dependency libraries as additional +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 @@ -136,3 +138,226 @@ assumes that CMake generates `Makefile`: $ cd ../curl-build $ make install + +# CMake build options + +- `BUILD_CURL_EXE`: Build curl executable. Default: `ON` +- `BUILD_EXAMPLES`: Build libcurl examples. Default: `ON` +- `BUILD_LIBCURL_DOCS`: Build libcurl man pages. Default: `ON` +- `BUILD_MISC_DOCS`: Build misc man pages (e.g. `curl-config` and `mk-ca-bundle`). Default: `ON` +- `BUILD_SHARED_LIBS`: Build shared libraries. Default: `ON` +- `BUILD_STATIC_CURL`: Build curl executable with static libcurl. Default: `OFF` +- `BUILD_STATIC_LIBS`: Build static libraries. Default: `OFF` +- `BUILD_TESTING`: Build tests. Default: `ON` +- `CURL_DEFAULT_SSL_BACKEND`: Override default TLS backend in MultiSSL builds. + Accepted values in order of default priority: + `wolfssl`, `gnutls`, `mbedtls`, `openssl`, `secure-transport`, `schannel`, `bearssl`, `rustls` +- `CURL_ENABLE_EXPORT_TARGET`: Enable CMake export target. Default: `ON` +- `CURL_HIDDEN_SYMBOLS`: Hide libcurl internal symbols (=hide all symbols that are not officially external). Default: `ON` +- `CURL_LIBCURL_SOVERSION`: Enable libcurl SOVERSION. Default: `ON` for supported platforms +- `CURL_LIBCURL_VERSIONED_SYMBOLS`: Enable libcurl versioned symbols. Default: `OFF` +- `CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX`: Override default versioned symbol prefix. Default: `_` or `MULTISSL_` +- `CURL_LTO`: Enable compiler Link Time Optimizations. Default: `OFF` +- `CURL_STATIC_CRT`: Build libcurl with static CRT with MSVC (`/MT`). Default: `OFF` +- `CURL_TARGET_WINDOWS_VERSION`: Minimum target Windows version as hex string. +- `CURL_TEST_BUNDLES`: Bundle `libtest` and `unittest` tests into single binaries. Default: `OFF` +- `CURL_WERROR`: Turn compiler warnings into errors. Default: `OFF` +- `ENABLE_CURLDEBUG`: Enable TrackMemory debug feature: Default: =`ENABLE_DEBUG` +- `ENABLE_CURL_MANUAL`: Build the man page for curl and enable its `-M`/`--manual` option. Default: `ON` +- `ENABLE_DEBUG`: Enable curl debug features (for developing curl itself). Default: `OFF` +- `IMPORT_LIB_SUFFIX`: Import library suffix. Default: `_imp` +- `LIBCURL_OUTPUT_NAME`: Basename of the curl library. Default: `libcurl` +- `PICKY_COMPILER`: Enable picky compiler options. Default: `ON` +- `STATIC_LIB_SUFFIX`: Static library suffix. Default: (empty) + +## CA bundle options + +- `CURL_CA_BUNDLE`: Path to the CA bundle. Set `none` to disable or `auto` for auto-detection. Default: `auto` +- `CURL_CA_EMBED`: Path to the CA bundle to embed in the curl tool. Default: (disabled) +- `CURL_CA_FALLBACK`: Use built-in CA store of TLS backend. Default: `OFF` +- `CURL_CA_PATH`: Location of default CA path. Set `none` to disable or `auto` for auto-detection. Default: `auto` +- `CURL_CA_SEARCH_SAFE`: Enable safe CA bundle search (within the curl tool directory) on Windows. Default: `OFF` + +## Enabling features + +- `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` +- `ENABLE_THREADED_RESOLVER`: Enable threaded DNS lookup. Default: `ON` if c-ares is not enabled +- `ENABLE_UNICODE`: Use the Unicode version of the Windows API functions. Default: `OFF` +- `ENABLE_UNIX_SOCKETS`: Enable Unix domain sockets support. Default: `ON` +- `USE_ECH`: Enable ECH support. Default: `OFF` +- `USE_HTTPSRR`: Enable HTTPS RR support for ECH (experimental). Default: `OFF` +- `USE_OPENSSL_QUIC`: Use OpenSSL and nghttp3 libraries for HTTP/3 support. Default: `OFF` + +## Disabling features + +- `CURL_DISABLE_ALTSVC`: Disable alt-svc support. Default: `OFF` +- `CURL_DISABLE_AWS`: Disable **aws-sigv4**. Default: `OFF` +- `CURL_DISABLE_BASIC_AUTH`: Disable Basic authentication. Default: `OFF` +- `CURL_DISABLE_BEARER_AUTH`: Disable Bearer authentication. Default: `OFF` +- `CURL_DISABLE_BINDLOCAL`: Disable local binding support. Default: `OFF` +- `CURL_DISABLE_CA_SEARCH`: Disable unsafe CA bundle search in PATH on Windows. Default: `OFF` +- `CURL_DISABLE_COOKIES`: Disable cookies support. Default: `OFF` +- `CURL_DISABLE_DICT`: Disable DICT. Default: `OFF` +- `CURL_DISABLE_DIGEST_AUTH`: Disable Digest authentication. Default: `OFF` +- `CURL_DISABLE_DOH`: Disable DNS-over-HTTPS. Default: `OFF` +- `CURL_DISABLE_FILE`: Disable FILE. Default: `OFF` +- `CURL_DISABLE_FORM_API`: Disable **form-api**: Default: =`CURL_DISABLE_MIME` +- `CURL_DISABLE_FTP`: Disable FTP. Default: `OFF` +- `CURL_DISABLE_GETOPTIONS`: Disable `curl_easy_options` API for existing options to `curl_easy_setopt`. Default: `OFF` +- `CURL_DISABLE_GOPHER`: Disable Gopher. Default: `OFF` +- `CURL_DISABLE_HEADERS_API`: Disable **headers-api** support. Default: `OFF` +- `CURL_DISABLE_HSTS`: Disable HSTS support. Default: `OFF` +- `CURL_DISABLE_HTTP`: Disable HTTP. Default: `OFF` +- `CURL_DISABLE_HTTP_AUTH`: Disable all HTTP authentication methods. Default: `OFF` +- `CURL_DISABLE_IMAP`: Disable IMAP. Default: `OFF` +- `CURL_DISABLE_INSTALL`: Disable installation targets. Default: `OFF` +- `CURL_DISABLE_IPFS`: Disable IPFS. Default: `OFF` +- `CURL_DISABLE_KERBEROS_AUTH`: Disable Kerberos authentication. Default: `OFF` +- `CURL_DISABLE_LDAP`: Disable LDAP. Default: `OFF` +- `CURL_DISABLE_LDAPS`: Disable LDAPS. Default: =`CURL_DISABLE_LDAP` +- `CURL_DISABLE_LIBCURL_OPTION`: Disable `--libcurl` option from the curl tool. Default: `OFF` +- `CURL_DISABLE_MIME`: Disable MIME support. Default: `OFF` +- `CURL_DISABLE_MQTT`: Disable MQTT. Default: `OFF` +- `CURL_DISABLE_NEGOTIATE_AUTH`: Disable negotiate authentication. Default: `OFF` +- `CURL_DISABLE_NETRC`: Disable netrc parser. Default: `OFF` +- `CURL_DISABLE_NTLM`: Disable NTLM support. Default: `OFF` +- `CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG`: Disable automatic loading of OpenSSL configuration. Default: `OFF` +- `CURL_DISABLE_PARSEDATE`: Disable date parsing. Default: `OFF` +- `CURL_DISABLE_POP3`: Disable POP3. Default: `OFF` +- `CURL_DISABLE_PROGRESS_METER`: Disable built-in progress meter. Default: `OFF` +- `CURL_DISABLE_PROXY`: Disable proxy support. Default: `OFF` +- `CURL_DISABLE_RTSP`: Disable RTSP. Default: `OFF` +- `CURL_DISABLE_SHA512_256`: Disable SHA-512/256 hash algorithm. Default: `OFF` +- `CURL_DISABLE_SHUFFLE_DNS`: Disable shuffle DNS feature. Default: `OFF` +- `CURL_DISABLE_SMB`: Disable SMB. Default: `OFF` +- `CURL_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` +- `CURL_DISABLE_TELNET`: Disable Telnet. Default: `OFF` +- `CURL_DISABLE_TFTP`: Disable TFTP. Default: `OFF` +- `CURL_DISABLE_VERBOSE_STRINGS`: Disable verbose strings. Default: `OFF` +- `CURL_DISABLE_WEBSOCKETS`: Disable WebSocket. Default: `OFF` +- `HTTP_ONLY`: Disable all protocols except HTTP (This overrides all `CURL_DISABLE_*` options). Default: `OFF` + +## Environment + +- `CI`: Assume running under CI if set. +- `CURL_BUILDINFO`: Print `buildinfo.txt` if set. +- `CURL_CI`: Assume running under CI if set. + +## CMake options + +- `CMAKE_DEBUG_POSTFIX`: Default: `-d` +- `CMAKE_IMPORT_LIBRARY_SUFFIX` (see CMake) +- `CMAKE_INSTALL_BINDIR` (see CMake) +- `CMAKE_INSTALL_INCLUDEDIR` (see CMake) +- `CMAKE_INSTALL_LIBDIR` (see CMake) +- `CMAKE_INSTALL_PREFIX` (see CMake) +- `CMAKE_STATIC_LIBRARY_SUFFIX` (see CMake) +- `CMAKE_UNITY_BUILD_BATCH_SIZE`: Set the number of sources in a "unity" unit. Default: `0` (all) +- `CMAKE_UNITY_BUILD`: Enable "unity" (aka jumbo) builds. Default: `OFF` + +Details via CMake +[variables](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html) and +[install directories](https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html). + +## Dependencies + +- `CURL_BROTLI`: Use brotli. Default: `OFF` +- `CURL_USE_BEARSSL`: Enable BearSSL for SSL/TLS. Default: `OFF` +- `CURL_USE_GNUTLS`: Enable GnuTLS for SSL/TLS. Default: `OFF` +- `CURL_USE_GSASL`: Use libgsasl. Default: `OFF` +- `CURL_USE_GSSAPI`: Use GSSAPI implementation. Default: `OFF` +- `CURL_USE_LIBPSL`: Use libpsl. Default: `ON` +- `CURL_USE_LIBSSH2`: Use libssh2. Default: `ON` +- `CURL_USE_LIBSSH`: Use libssh. Default: `OFF` +- `CURL_USE_LIBUV`: Use libuv for event-based tests. Default: `OFF` +- `CURL_USE_MBEDTLS`: Enable mbedTLS for SSL/TLS. Default: `OFF` +- `CURL_USE_OPENSSL`: Enable OpenSSL for SSL/TLS. Default: `ON` if no other TLS backend was enabled. +- `CURL_USE_PKGCONFIG`: Enable `pkg-config` to detect dependencies. Default: `ON` for Unix, vcpkg, MinGW if not cross-compiling. +- `CURL_USE_RUSTLS`: Enable Rustls for SSL/TLS. Default: `OFF` +- `CURL_USE_SCHANNEL`: Enable Windows native SSL/TLS (Schannel). Default: `OFF` +- `CURL_USE_SECTRANSP`: Enable Apple OS native SSL/TLS (Secure Transport). Default: `OFF` +- `CURL_USE_WOLFSSH`: Use wolfSSH. Default: `OFF` +- `CURL_USE_WOLFSSL`: Enable wolfSSL for SSL/TLS. Default: `OFF` +- `CURL_ZLIB`: Use zlib (`ON`, `OFF` or `AUTO`). Default: `AUTO` +- `CURL_ZSTD`: Use zstd. Default: `OFF` +- `ENABLE_ARES`: Enable c-ares support. Default: `OFF` +- `USE_APPLE_IDN`: Use Apple built-in IDN support. Default: `OFF` +- `USE_LIBIDN2`: Use libidn2 for IDN support. Default: `ON` +- `USE_LIBRTMP`: Enable librtmp from rtmpdump. Default: `OFF` +- `USE_MSH3`: Use msh3/msquic library for HTTP/3 support. Default: `OFF` +- `USE_NGHTTP2`: Use nghttp2 library. Default: `ON` +- `USE_NGTCP2`: Use ngtcp2 and nghttp3 libraries for HTTP/3 support. Default: `OFF` +- `USE_QUICHE`: Use quiche library for HTTP/3 support. Default: `OFF` +- `USE_WIN32_IDN`: Use WinIDN for IDN support. Default: `OFF` +- `USE_WIN32_LDAP`: Use Windows LDAP implementation. Default: `ON` + +## Dependency options (via CMake) + +- `OPENSSL_ROOT_DIR`: Set this variable to the root installation of OpenSSL (and forks). +- `ZLIB_INCLUDE_DIR`: The zlib include directory. +- `ZLIB_LIBRARY`: Path to `zlib` library. + +## Dependency options + +- `PERL_EXECUTABLE` Perl binary used throughout the build and tests. +- `BEARSSL_INCLUDE_DIR`: The BearSSL include directory. +- `BEARSSL_LIBRARY`: Path to `bearssl` library. +- `BROTLI_INCLUDE_DIR`: The brotli include directory. +- `BROTLICOMMON_LIBRARY`: Path to `brotlicommon` library. +- `BROTLIDEC_LIBRARY`: Path to `brotlidec` library. +- `CARES_INCLUDE_DIR`: The c-ares include directory. +- `CARES_LIBRARY`: Path to `cares` library. +- `GSS_ROOT_DIR`: Set this variable to the root installation of GSS. (also supported as environment) +- `LDAP_LIBRARY`: Name or full path to `ldap` library. Default: `ldap` +- `LDAP_LBER_LIBRARY`: Name or full path to `lber` library. Default: `lber` +- `LDAP_INCLUDE_DIR`: Path to LDAP include directory. +- `LIBGSASL_INCLUDE_DIR`: The libgsasl include directory. +- `LIBGSASL_LIBRARY`: Path to `libgsasl` library. +- `LIBIDN2_INCLUDE_DIR`: The libidn2 include directory. +- `LIBIDN2_LIBRARY`: Path to `libidn2` library. +- `LIBPSL_INCLUDE_DIR`: The libpsl include directory. +- `LIBPSL_LIBRARY`: Path to `libpsl` library. +- `LIBSSH_INCLUDE_DIR`: The libssh include directory. +- `LIBSSH_LIBRARY`: Path to `libssh` library. +- `LIBSSH2_INCLUDE_DIR`: The libssh2 include directory. +- `LIBSSH2_LIBRARY`: Path to `libssh2` library. +- `LIBUV_INCLUDE_DIR`: The libuv include directory. +- `LIBUV_LIBRARY`: Path to `libuv` library. +- `MSH3_INCLUDE_DIR`: The msh3 include directory. +- `MSH3_LIBRARY`: Path to `msh3` library. +- `MBEDTLS_INCLUDE_DIR`: The mbedTLS include directory. +- `MBEDTLS_LIBRARY`: Path to `mbedtls` library. +- `MBEDX509_LIBRARY`: Path to `mbedx509` library. +- `MBEDCRYPTO_LIBRARY`: Path to `mbedcrypto` library. +- `NGHTTP2_INCLUDE_DIR`: The nghttp2 include directory. +- `NGHTTP2_LIBRARY`: Path to `nghttp2` library. +- `NGHTTP3_INCLUDE_DIR`: The nghttp3 include directory. +- `NGHTTP3_LIBRARY`: Path to `nghttp3` library. +- `NGTCP2_INCLUDE_DIR`: The ngtcp2 include directory. +- `NGTCP2_LIBRARY`: Path to `ngtcp2` library. +- `NETTLE_INCLUDE_DIR`: The nettle include directory. +- `NETTLE_LIBRARY`: Path to `nettle` library. +- `QUICHE_INCLUDE_DIR`: The quiche include directory. +- `QUICHE_LIBRARY`: Path to `quiche` library. +- `RUSTLS_INCLUDE_DIR`: The Rustls include directory. +- `RUSTLS_LIBRARY`: Path to `rustls` library. +- `WOLFSSH_INCLUDE_DIR`: The wolfSSH include directory. +- `WOLFSSH_LIBRARY`: Path to `wolfssh` library. +- `WOLFSSL_INCLUDE_DIR`: The wolfSSL include directory. +- `WOLFSSL_LIBRARY`: Path to `wolfssl` library. +- `ZSTD_INCLUDE_DIR`: The zstd include directory. +- `ZSTD_LIBRARY`: Path to `zstd` library. + +## Test tools + +- `APACHECTL`: Default: `apache2ctl` +- `APXS`: Default: `apxs` +- `CADDY`: Default: `caddy` +- `HTTPD_NGHTTPX`: Default: `nghttpx` +- `HTTPD`: Default: `apache2` +- `TEST_NGHTTPX`: Default: `nghttpx` +- `VSFTPD`: Default: `vsftps` diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 85de7a1ed1c37b..bfcbd2e7866272 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -570,18 +570,18 @@ that are not automatically detected: This is a probably incomplete list of known CPU architectures and operating systems that curl has been compiled for. If you know a system curl compiles -and runs on, that is not listed, please let us know! +and runs on, that is not listed, please let us know. ## 101 Operating Systems AIX, AmigaOS, Android, ArcoOS, Aros, Atari FreeMiNT, BeOS, Blackberry 10, Blackberry Tablet OS, Cell OS, CheriBSD, Chrome OS, Cisco IOS, DG/UX, Dragonfly BSD, DR DOS, eCOS, FreeBSD, FreeDOS, FreeRTOS, Fuchsia, Garmin OS, - Genode, Haiku, HardenedBSD, HP-UX, Hurd, Illumos, Integrity, iOS, ipadOS, IRIX, + Genode, Haiku, HardenedBSD, HP-UX, Hurd, illumos, Integrity, iOS, ipadOS, IRIX, Linux, Lua RTOS, Mac OS 9, macOS, Mbed, Meego, Micrium, MINIX, Moblin, MorphOS, MPE/iX, MS-DOS, NCR MP-RAS, NetBSD, Netware, NextStep, Nintendo Switch, NonStop OS, NuttX, OpenBSD, OpenStep, Orbis OS, OS/2, OS/400, OS21, Plan 9, - PlayStation Portable, QNX, Qubes OS, ReactOS, Redox, RICS OS, ROS, RTEMS, + PlayStation Portable, QNX, Qubes OS, ReactOS, Redox, RISC OS, ROS, RTEMS, Sailfish OS, SCO Unix, Serenity, SINIX-Z, SkyOS, Solaris, Sortix, SunOS, Syllable OS, Symbian, Tizen, TPF, Tru64, tvOS, ucLinux, Ultrix, UNICOS, UnixWare, VMS, vxWorks, watchOS, Wear OS, WebOS, Wii system software, Wii U, diff --git a/docs/KNOWN_BUGS b/docs/KNOWN_BUGS index 2b04630a9ea151..5a1e5eeaada327 100644 --- a/docs/KNOWN_BUGS +++ b/docs/KNOWN_BUGS @@ -36,6 +36,7 @@ problems may have been fixed or changed somewhat since this was written. 5. Build and portability issues 5.1 OS400 port requires deprecated IBM library 5.2 curl-config --libs contains private details + 5.3 LDFLAGS passed too late making libs linked incorrectly 5.6 Cygwin: make install installs curl-config.1 twice 5.11 configure --with-gssapi with Heimdal is ignored on macOS 5.12 flaky CI builds @@ -225,6 +226,14 @@ problems may have been fixed or changed somewhat since this was written. that might be needed only for building libcurl. Further, curl-config --cflags suffers from the same effects with CFLAGS/CPPFLAGS. +5.3 LDFLAGS passed too late making libs linked incorrectly + + Compiling latest curl on HP-UX and linking against a custom OpenSSL (which is + on the default loader/linker path), fails because the generated Makefile has + LDFLAGS passed on after LIBS. + + See https://github.com/curl/curl/issues/14893 + 5.6 Cygwin: make install installs curl-config.1 twice https://github.com/curl/curl/issues/8839 diff --git a/docs/MAIL-ETIQUETTE.md b/docs/MAIL-ETIQUETTE.md index e3cf702be88003..3de77b17bf7a53 100644 --- a/docs/MAIL-ETIQUETTE.md +++ b/docs/MAIL-ETIQUETTE.md @@ -232,7 +232,7 @@ We allow subscribers to subscribe to the "digest" version of the mailing lists. A digest is a collection of mails lumped together in one single mail. Should you decide to reply to a mail sent out as a digest, there are two -things you MUST consider if you really really cannot subscribe normally +things you MUST consider if you really, really cannot subscribe normally instead: Cut off all mails and chatter that is not related to the mail you want to diff --git a/docs/MANUAL.md b/docs/MANUAL.md index 33596663c1121b..35a944849a8bbe 100644 --- a/docs/MANUAL.md +++ b/docs/MANUAL.md @@ -274,7 +274,7 @@ To get even more details and information on what curl does, try using the `--trace` or `--trace-ascii` options with a given filename to log to, like this: - curl --trace trace.txt www.haxx.se + curl --trace my-trace.txt www.haxx.se ## Detailed Information diff --git a/docs/RELEASE-PROCEDURE.md b/docs/RELEASE-PROCEDURE.md index 043e3afd0cda4c..80718b99fc92ad 100644 --- a/docs/RELEASE-PROCEDURE.md +++ b/docs/RELEASE-PROCEDURE.md @@ -105,14 +105,12 @@ push for it. Coming dates ------------ -Based on the description above, here are some planned release dates (at the -time of this writing): - -- September 11, 2024 -- November 6, 2024 -- January 8, 2025 -- March 5, 2025 -- April 30, 2025 -- June 25, 2025 -- August 20, 2025 -- October 15, 2025 +Based on the description above, here are some planned future release dates: + +- December 11, 2024 +- February 5, 2025 +- April 2, 2025 +- May 28, 2025 +- July 23, 2025 +- September 17, 2025 +- November 12, 2025 diff --git a/docs/SPONSORS.md b/docs/SPONSORS.md index bb1109760486a3..c9cf42a7f65c27 100644 --- a/docs/SPONSORS.md +++ b/docs/SPONSORS.md @@ -44,4 +44,4 @@ gambling, pornography, social media manipulation etc. ## Past Sponsors Sponsors that stop paying are considered *Past Sponsors* and are not displayed -on the sponsor page anymore. We thank you for your contributions! +on the sponsor page anymore. We thank you for your contributions. diff --git a/docs/SSL-PROBLEMS.md b/docs/SSL-PROBLEMS.md index 26deed3911a975..620392c4ea7d99 100644 --- a/docs/SSL-PROBLEMS.md +++ b/docs/SSL-PROBLEMS.md @@ -44,7 +44,7 @@ SPDX-License-Identifier: curl when connecting to make the connection succeed. An additional complication can be that modern SSL libraries sometimes are - built with support for older SSL and TLS versions disabled! + built with support for older SSL and TLS versions disabled. All versions of SSL and the TLS versions before 1.2 are considered insecure and should be avoided. Use TLS 1.2 or later. diff --git a/docs/THANKS b/docs/THANKS index 66cba949fc36a7..959604bb9a7dd5 100644 --- a/docs/THANKS +++ b/docs/THANKS @@ -24,6 +24,7 @@ Abhinav Singh Abram Pousada accountantM on github AceCrow on Github +ad-chaos on github ad0p on github Adam Averay Adam Barclay @@ -501,6 +502,7 @@ Chris Paulson-Ellis Chris Roberts Chris Sauer Chris Smowton +Chris Stubbs Chris Swan Chris Talbot Chris Webb @@ -564,6 +566,7 @@ Colin O'Dell Colin Watson Colm Buckley Colman Mbuya +Colton Willey Constantine Sapuntzakis consulion on github coralw on github @@ -727,6 +730,7 @@ Denis Feklushkin Denis Goleshchikhin Denis Laxalde Denis Ollier +Deniz SÃļkmen Dennis Clarke Dennis Felsing dependabot[bot] @@ -819,6 +823,7 @@ Dustin Howett Dusty Mabe Duy Phan Thanh Dwarakanath Yadavalli +Dylam De La Torre Dylan Anthony Dylan Ellicott Dylan Salisbury @@ -863,7 +868,9 @@ Elmira A Semenova Elms Eloy Degen elsamuko on github +elvinasp on github emanruse on github +Emanuel Komínek Emanuele Bovisio Emanuele Torre Emil Engler @@ -1407,6 +1414,7 @@ Jishan Shaikh Jiwoo Park Jiří Bok Jiří MalÃĄk +jkamp-aws on github jmdavitt on github jnbr on github Jocelyn Jaubert @@ -1485,6 +1493,7 @@ Jon Torrey Jon Travis Jon Turner Jon Wilkes +Jonas 'Sortie' Termansen Jonas BÃŧlow Jonas Forsman Jonas Haag @@ -1656,6 +1665,7 @@ Konstantin Kuzov Konstantin Vlasov KotlinIsland on github kotoriぎねこ +koujaz on github kouzhudong on github Kovalkov Dmitrii kpcyrd on github @@ -1759,6 +1769,7 @@ locpyl-tidnyd on github Loganaden Velvindron Loic Dachary lolbinarycat on github +lomberd2 on github LoRd_MuldeR Loren Kirkby Lorenzo Miniero @@ -1803,6 +1814,7 @@ Maciej Domanski Maciej Karpiuk Maciej Puzio Maciej W. Rozycki +MacKenzie madblobfish on github MaeIsBad on github magisterquis on hackerone @@ -1919,6 +1931,7 @@ Martin Ågren martinevsky Marty Kuhrt Maruko +Marwan Yassini Masaya Suzuki masbug on github Massimiliano Fantuzzi @@ -2095,9 +2108,11 @@ Mohun Biswas momala454 on github Momoka Yamamoto MonkeybreadSoftware on github +Montg0mery on github moohoorama on github Morgan Willcock Moritz Buhl +Moritz KnÃŧsel Morten Minde Neergaard Mostyn Bramley-Moore Moti Avrahami @@ -2137,8 +2152,10 @@ Neil Dunbar Neil Kolban Neil Spring nekopsykose on github +Nemos2024 on github neutric on github nevv on HackerOne/curl +newfunction Niall McGee Niall O'Reilly nian6324 on github @@ -2161,6 +2178,7 @@ nico-abram on github Nicolas Berloquin Nicolas Croiset Nicolas François +Nicolas George Nicolas Grekas Nicolas Guillier Nicolas Morey-Chaisemartin @@ -2389,6 +2407,7 @@ Pierre Chapuis Pierre Joye Pierre Yager Pierre Ynard +Pierre-Etienne Meunier Pierre-Yves Bigourdan Pierrick Charron Piotr Dobrogost @@ -2558,6 +2577,7 @@ Robert Foreman Robert Iakobashvili Robert Kolcun Robert Linden +Robert Maynard Robert Moreton Robert Olson Robert Prag @@ -2697,6 +2717,7 @@ Sebastian Neubauer Sebastian Pohlschmidt Sebastian Rasmussen Sebastian Sterk +Sebastian Walz selmelc on hackerone SendSonS on github Senthil Raja Velu @@ -2763,6 +2784,7 @@ Simon Legner Simon Liu Simon Warta simplerobot on github +Sinkevich Artem Siva Sivaraman Slaven Rezić SLDiggie on github @@ -2958,6 +2980,7 @@ Tk Xiong tlahn on github tmkk on github Tobias Blomberg +Tobias Bora Tobias Gabriel Tobias Hieta Tobias Hintze @@ -2969,6 +2992,7 @@ Tobias Nyholm Tobias RundstrÃļm Tobias Schaefer Tobias Stoeckmann +Tobias Wendorff Toby Peterson Todd A Ouska Todd Kaufmann @@ -3102,6 +3126,7 @@ Vojtech Janota Vojtech Minarik Vojtěch KrÃĄl Volker Schmid +Vollstrecker on github Vsevolod Novikov vshmuk on hackerone vulnerabilityspotter on hackerone @@ -3146,6 +3171,7 @@ Wolf Vollprecht Wouter Van Rooy Wu Yongzheng Wu Zheng +wxiaoguang on github Wyatt O'Day Wyatt OĘŧDay x2018 on github @@ -3220,6 +3246,7 @@ Zhibiao Wu zhihaoy on github Zhouyihai Ding ZimCodes on github +zjyhjqs zloi-user on github zmcx16 on github Zmey Petroff diff --git a/docs/THANKS-filter b/docs/THANKS-filter index 1b97a8b39638e0..74676e711c9f92 100644 --- a/docs/THANKS-filter +++ b/docs/THANKS-filter @@ -152,3 +152,5 @@ s/Karthikdasari0423\z/Karthikdasari0423 on github/ s/niallor on github/Niall O'Reilly/ s/RuurdBeerstra on github/Ruurd Beerstra/ s/Smackd0wn\z/Smackd0wn on github/ +s/Testclutch// +s/edmcln\z/edmcln on github/ diff --git a/docs/TODO b/docs/TODO index 778ae21b6cb02e..cdc9d5f9b4adb4 100644 --- a/docs/TODO +++ b/docs/TODO @@ -59,7 +59,6 @@ 3. Documentation 3.1 Improve documentation about fork safety - 3.2 Provide cmake config-file 4. FTP 4.1 HOST @@ -114,6 +113,7 @@ 13.1 TLS-PSK with OpenSSL 13.2 TLS channel binding 13.3 Defeat TLS fingerprinting + 13.4 Consider OCSP stapling by default 13.5 Export session ids 13.6 Provide callback for cert verification 13.7 Less memory massaging with Schannel @@ -546,12 +546,6 @@ See https://github.com/curl/curl/issues/6968 -3.2 Provide cmake config-file - - A config-file package is a set of files provided by us to allow applications - to write cmake scripts to find and use libcurl easier. See - https://github.com/curl/curl/issues/885 - 4. FTP 4.1 HOST @@ -824,6 +818,14 @@ sometimes possible to circumvent TLS fingerprinting by servers. The TLS extension order is of course not the only way to fingerprint a client. +13.4 Consider OCSP stapling by default + + Treat a negative response a reason for aborting the connection. Since OCSP + stapling is presumed to get used much less in the future when Let's Encrypt + drops the OCSP support, the benefit of this might however be limited. + + https://github.com/curl/curl/issues/15483 + 13.5 Export session ids Add an interface to libcurl that enables "session IDs" to get diff --git a/docs/TheArtOfHttpScripting.md b/docs/TheArtOfHttpScripting.md index 659fbdd11b6ef5..28314ea9097067 100644 --- a/docs/TheArtOfHttpScripting.md +++ b/docs/TheArtOfHttpScripting.md @@ -456,6 +456,8 @@ SPDX-License-Identifier: curl it is time to set the User Agent field to fool the server into thinking you are one of those browsers. + By default, curl uses curl/VERSION, such as User-Agent: curl/8.11.0. + To make curl look like Internet Explorer 5 on a Windows 2000 box: curl --user-agent "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" [URL] @@ -571,7 +573,7 @@ SPDX-License-Identifier: curl Curl supports encrypted fetches when built to use a TLS library and it can be built to use one out of a fairly large set of libraries - `curl -V` shows - which one your curl was built to use (if any!). To get a page from an HTTPS + which one your curl was built to use (if any). To get a page from an HTTPS server, simply run curl like: curl https://secure.example.com diff --git a/docs/URL-SYNTAX.md b/docs/URL-SYNTAX.md index 3c921f8570d15c..61682f4252f3e6 100644 --- a/docs/URL-SYNTAX.md +++ b/docs/URL-SYNTAX.md @@ -32,7 +32,7 @@ unlikely that multiple parsers treat URLs the same way. Due to the inherent differences between URL parser implementations, it is considered a security risk to mix different implementations and assume the -same behavior! +same behavior. For example, if you use one parser to check if a URL uses a good hostname or the correct auth field, and then pass on that same URL to a *second* parser, diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index a7f635d8d9a45e..3bcffa49fca4b4 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -281,6 +281,7 @@ DPAGES = \ tftp-blksize.md \ tftp-no-options.md \ time-cond.md \ + tls-earlydata.md \ tls-max.md \ tls13-ciphers.md \ tlsauthtype.md \ diff --git a/docs/cmdline-opts/_EXITCODES.md b/docs/cmdline-opts/_EXITCODES.md index c5a928b5904da5..a16f475276aa88 100644 --- a/docs/cmdline-opts/_EXITCODES.md +++ b/docs/cmdline-opts/_EXITCODES.md @@ -105,7 +105,7 @@ Too many redirects. When following redirects, curl hit the maximum amount. ## 48 Unknown option specified to libcurl. This indicates that you passed a weird option to curl that was passed on to libcurl and rejected. Read up in the -manual! +manual. ## 49 Malformed telnet option. ## 52 diff --git a/docs/cmdline-opts/_OPTIONS.md b/docs/cmdline-opts/_OPTIONS.md index ef208ade0f48c8..b6b75b3f345e3d 100644 --- a/docs/cmdline-opts/_OPTIONS.md +++ b/docs/cmdline-opts/_OPTIONS.md @@ -1,6 +1,7 @@ # OPTIONS + Options start with one or two dashes. Many of the options require an additional value next to them. If provided text does not start with a dash, it is presumed to be and treated as a URL. diff --git a/docs/cmdline-opts/data-urlencode.md b/docs/cmdline-opts/data-urlencode.md index 4edfb4d764f6a6..b4680e61acae11 100644 --- a/docs/cmdline-opts/data-urlencode.md +++ b/docs/cmdline-opts/data-urlencode.md @@ -30,7 +30,7 @@ curl using one of the following syntaxes: ## content URL-encode the content and pass that on. Just be careful so that the content does not contain any `=` or `@` symbols, as that makes the syntax match one of -the other cases below! +the other cases below. ## =content URL-encode the content and pass that on. The preceding `=` symbol is not diff --git a/docs/cmdline-opts/ech.md b/docs/cmdline-opts/ech.md index 6c2ba31cb69d83..ed16cd81126b66 100644 --- a/docs/cmdline-opts/ech.md +++ b/docs/cmdline-opts/ech.md @@ -20,34 +20,33 @@ Specifies how to do ECH (Encrypted Client Hello). The values allowed for \ can be: -## "false" -Do not attempt ECH +## `false` -## "grease" +Do not attempt ECH. The is the default. + +## `grease` Send a GREASE ECH extension -## "true" +## `true` Attempt ECH if possible, but do not fail if ECH is not attempted. (The connection fails if ECH is attempted but fails.) -## "hard" +## `hard` -Attempt ECH and fail if that is not possible. -ECH only works with TLS 1.3 and also requires using -DoH or providing an ECHConfigList on the command line. +Attempt ECH and fail if that is not possible. ECH only works with TLS 1.3 and +also requires using DoH or providing an ECHConfigList on the command line. -## "ecl:" +## `ecl:` A base64 encoded ECHConfigList that is used for ECH. -## "pn:" +## `pn:` -A name to use to over-ride the `public_name` field of an ECHConfigList -(only available with OpenSSL TLS support) +A name to use to over-ride the `public_name` field of an ECHConfigList (only +available with OpenSSL TLS support) -## Errors +## -Most errors cause error -*CURLE_ECH_REQUIRED* (101). +Most ECH related errors cause error *CURLE_ECH_REQUIRED* (101). diff --git a/docs/cmdline-opts/head.md b/docs/cmdline-opts/head.md index be4dbb87fd6a0c..353ef9a0118dce 100644 --- a/docs/cmdline-opts/head.md +++ b/docs/cmdline-opts/head.md @@ -18,6 +18,6 @@ Example: # `--head` -Fetch the headers only! HTTP-servers feature the command HEAD which this uses -to get nothing but the header of a document. When used on an FTP or FILE file, +Fetch the headers only. HTTP-servers feature the command HEAD which this uses +to get nothing but the header of a document. When used on an FTP or FILE URL, curl displays the file size and last modification time only. diff --git a/docs/cmdline-opts/json.md b/docs/cmdline-opts/json.md index 8056e62e7cf73c..7763d81ee0ba89 100644 --- a/docs/cmdline-opts/json.md +++ b/docs/cmdline-opts/json.md @@ -24,7 +24,7 @@ Example: Sends the specified JSON data in a POST request to the HTTP server. --json works as a shortcut for passing on these three options: - --data [arg] + --data-binary [arg] --header "Content-Type: application/json" --header "Accept: application/json" diff --git a/docs/cmdline-opts/libcurl.md b/docs/cmdline-opts/libcurl.md index 60af6054066bab..e37e5aa0fadd26 100644 --- a/docs/cmdline-opts/libcurl.md +++ b/docs/cmdline-opts/libcurl.md @@ -18,4 +18,4 @@ Example: Append this option to any ordinary curl command line, and you get libcurl-using C source code written to the file that does the equivalent of -what your command-line operation does! +what your command-line operation does. diff --git a/docs/cmdline-opts/proxy-tls13-ciphers.md b/docs/cmdline-opts/proxy-tls13-ciphers.md index 7a03b0f7c9cb50..72bae4e75d4727 100644 --- a/docs/cmdline-opts/proxy-tls13-ciphers.md +++ b/docs/cmdline-opts/proxy-tls13-ciphers.md @@ -29,5 +29,5 @@ https://curl.se/docs/ssl-ciphers.html This option is used when curl is built to use OpenSSL 1.1.1 or later, Schannel, wolfSSL, or mbedTLS 3.6.0 or later. -Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites where set +Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites were set by using the --proxy-ciphers option. diff --git a/docs/cmdline-opts/proxy.md b/docs/cmdline-opts/proxy.md index 529d3369fb2b1d..afaa29837eeb85 100644 --- a/docs/cmdline-opts/proxy.md +++ b/docs/cmdline-opts/proxy.md @@ -56,3 +56,7 @@ password. When a proxy is used, the active FTP mode as set with --ftp-port, cannot be used. + +Doing FTP over an HTTP proxy without --proxytunnel makes curl do HTTP with an +FTP URL over the proxy. For such transfers, common FTP specific options do not +work, including --ftp-ssl-reqd and --ftp-ssl-control. diff --git a/docs/cmdline-opts/range.md b/docs/cmdline-opts/range.md index abfdf216f8f814..c2cee2e1f6ccb8 100644 --- a/docs/cmdline-opts/range.md +++ b/docs/cmdline-opts/range.md @@ -42,7 +42,7 @@ specifies two separate 100-byte ranges(*) (HTTP) ## (*) = NOTE that these make the server reply with a multipart response, which -is returned as-is by curl! Parsing or otherwise transforming this response is +is returned as-is by curl. Parsing or otherwise transforming this response is the responsibility of the caller. Only digit characters (0-9) are valid in the 'start' and 'stop' fields of the diff --git a/docs/cmdline-opts/show-headers.md b/docs/cmdline-opts/show-headers.md index 126610f7cf397e..b2a45d6c2fb3ef 100644 --- a/docs/cmdline-opts/show-headers.md +++ b/docs/cmdline-opts/show-headers.md @@ -10,6 +10,7 @@ Added: 4.8 Multi: boolean See-also: - verbose + - dump-header Example: - -i $URL --- @@ -20,6 +21,9 @@ Show response headers in the output. HTTP response headers can include things like server name, cookies, date of the document, HTTP version and more. With 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. + 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/tls-earlydata.md b/docs/cmdline-opts/tls-earlydata.md new file mode 100644 index 00000000000000..8482f809ec0630 --- /dev/null +++ b/docs/cmdline-opts/tls-earlydata.md @@ -0,0 +1,41 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: tls-earlydata +Help: Allow use of TLSv1.3 early data (0RTT) +Protocols: TLS +Added: 8.11.0 +Category: tls +Multi: boolean +See-also: + - tlsv1.3 + - tls-max +Example: + - --tls-earlydata $URL +--- + +# `--tls-earlydata` + +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 is used when curl is built to use GnuTLS. + +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 +a session in a previous request, early data cannot be used. + +When a new connection is initiated with a known TLSv1.3 session, and that +session announced early data support, the first request on this connection is +sent *before* the TLS handshake is complete. While the early data is also +encrypted, it is not protected against replays. An attacker can send +your early data to the server again and the server would accept it. + +If your request contacts a public server and only retrieves a file, there +may be no harm in that. If the first request orders a refrigerator +for you, it is probably not a good idea to use early data for it. curl +cannot deduce what the security implications of your requests actually +are and make this decision for you. + +**WARNING**: this option has security implications. See above for more +details. diff --git a/docs/cmdline-opts/tls13-ciphers.md b/docs/cmdline-opts/tls13-ciphers.md index 0f1ff33ce8c18e..43220af4da73f0 100644 --- a/docs/cmdline-opts/tls13-ciphers.md +++ b/docs/cmdline-opts/tls13-ciphers.md @@ -27,5 +27,5 @@ https://curl.se/docs/ssl-ciphers.html This option is used when curl is built to use OpenSSL 1.1.1 or later, Schannel, wolfSSL, or mbedTLS 3.6.0 or later. -Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites where set +Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites were set by using the --ciphers option. diff --git a/docs/cmdline-opts/url.md b/docs/cmdline-opts/url.md index 851f0abacacacb..d19c73ecbcd406 100644 --- a/docs/cmdline-opts/url.md +++ b/docs/cmdline-opts/url.md @@ -16,17 +16,19 @@ Example: # `--url` -Specify a URL to fetch. +Specify a URL to fetch or send data to. -If the given URL is missing a scheme name (such as `http://` or `ftp://` etc) -then curl makes a guess based on the host. If the outermost subdomain name -matches DICT, FTP, IMAP, LDAP, POP3 or SMTP then that protocol is used, -otherwise HTTP is used. Guessing can be avoided by providing a full URL -including the scheme, or disabled by setting a default protocol, see ---proto-default for details. +If the given URL is missing a scheme (such as `http://` or `ftp://` etc) curl +guesses which scheme to use based on the hostname. If the outermost subdomain +name matches DICT, FTP, IMAP, LDAP, POP3 or SMTP case insensitively, then that +protocol is used, otherwise it assumes HTTP. Scheme guessing can be avoided by +providing a full URL including the scheme, or disabled by setting a default +protocol, see --proto-default for details. -To control where this URL is written, use the --output or the --remote-name -options. +To control where the contents of a retrieved URL is written instead of the +default stdout, use the --output or the --remote-name options. When retrieving +multiple URLs in a single invoke, each provided URL needs its own dedicated +destination option unless --remote-name-all is used. -**WARNING**: On Windows, particular `file://` accesses can be converted to -network accesses by the operating system. Beware! +On Windows, `file://` accesses can be converted to network accesses by the +operating system. diff --git a/docs/cmdline-opts/user-agent.md b/docs/cmdline-opts/user-agent.md index 29b475857ad958..a24bd28449ad80 100644 --- a/docs/cmdline-opts/user-agent.md +++ b/docs/cmdline-opts/user-agent.md @@ -25,3 +25,5 @@ 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 single space (" "). + +By default, curl uses curl/VERSION, such as User-Agent: curl/`%VERSION`. diff --git a/docs/cmdline-opts/version.md b/docs/cmdline-opts/version.md index 948e9672f19227..0597f84a27c342 100644 --- a/docs/cmdline-opts/version.md +++ b/docs/cmdline-opts/version.md @@ -44,7 +44,7 @@ curl was built with support for character set conversions (like EBCDIC) ## `Debug` This curl uses a libcurl built with Debug. This enables more error-tracking -and memory debugging etc. For curl-developers only! +and memory debugging etc. For curl-developers only. ## `ECH` ECH support is present. diff --git a/docs/cmdline-opts/xattr.md b/docs/cmdline-opts/xattr.md index 9dd7dc585f3492..0c3bb753ae5db0 100644 --- a/docs/cmdline-opts/xattr.md +++ b/docs/cmdline-opts/xattr.md @@ -17,7 +17,7 @@ Example: # `--xattr` When saving output to a file, tell curl to store file metadata in extended -file attributes. Currently, the URL is stored in the `xdg.origin.url` -attribute and, for HTTP, the content type is stored in the `mime_type` -attribute. If the file system does not support extended attributes, a warning -is issued. +file attributes. Currently, `curl` is stored in the `creator` attribute, +the URL is stored in the `xdg.origin.url` attribute and, for HTTP, the content +type is stored in the `mime_type` attribute. If the file system does not +support extended attributes, a warning is issued. diff --git a/docs/examples/README.md b/docs/examples/README.md index 3f95f03c006cf4..a6a31c9388a091 100644 --- a/docs/examples/README.md +++ b/docs/examples/README.md @@ -21,12 +21,12 @@ first. Most examples should build fine using a command line like this: - `curl-config --cc --cflags --libs` -o example example.c + `curl-config --cc --cflags --libs` -o example-my example.c Some compilers do not like having the arguments in this order but instead want you do reorganize them like: - `curl-config --cc` -o example example.c `curl-config --cflags --libs` + `curl-config --cc` -o example-my example.c `curl-config --cflags --libs` **Please** do not use the `curl.se` site as a test target for your libcurl applications/experiments. Even if some of the examples use that site as a URL diff --git a/docs/examples/http2-upload.c b/docs/examples/http2-upload.c index 30f0a95a708000..e804fb76a97f97 100644 --- a/docs/examples/http2-upload.c +++ b/docs/examples/http2-upload.c @@ -53,6 +53,7 @@ #ifdef _MSC_VER #define gettimeofday(a, b) my_gettimeofday((a), (b)) +static int my_gettimeofday(struct timeval *tp, void *tzp) { (void)tzp; diff --git a/docs/internals/HYPER.md b/docs/internals/HYPER.md index 591b6fc30f638f..bbaa4e4d59b80e 100644 --- a/docs/internals/HYPER.md +++ b/docs/internals/HYPER.md @@ -9,7 +9,7 @@ SPDX-License-Identifier: curl Hyper is a separate HTTP library written in Rust. curl can be told to use this library as a backend to deal with HTTP. -## Experimental! +## EXPERIMENTAL Hyper support in curl is considered **EXPERIMENTAL** until further notice. It needs to be explicitly enabled at build-time. diff --git a/docs/internals/MQTT.md b/docs/internals/MQTT.md index a1bc4b349ef3bc..90d641b30c6887 100644 --- a/docs/internals/MQTT.md +++ b/docs/internals/MQTT.md @@ -9,19 +9,37 @@ SPDX-License-Identifier: curl ## Usage A plain "GET" subscribes to the topic and prints all published messages. + Doing a "POST" publishes the post data to the topic and exits. + +### Subscribing + +Command usage: + + curl mqtt://host/topic + Example subscribe: curl mqtt://host.home/bedroom/temp +This will send an MQTT SUBSCRIBE packet for the topic `bedroom/temp` and listen in for incoming PUBLISH packets. + +### Publishing + +Command usage: + + curl -d payload mqtt://host/topic + Example publish: curl -d 75 mqtt://host.home/bedroom/dimmer +This will send an MQTT PUBLISH packet to the topic `bedroom/dimmer` with the payload `75`. + ## What does curl deliver as a response to a subscribe -It outputs two bytes topic length (MSB | LSB), the topic followed by the +Whenever a PUBLISH packet is received, curl outputs two bytes topic length (MSB | LSB), the topic followed by the payload. ## Caveats diff --git a/docs/internals/NEW-PROTOCOL.md b/docs/internals/NEW-PROTOCOL.md index be7fc9846527ee..35beba6edb13ca 100644 --- a/docs/internals/NEW-PROTOCOL.md +++ b/docs/internals/NEW-PROTOCOL.md @@ -101,7 +101,7 @@ peculiarities of the protocol. It needs documentation. Maybe it even needs some internal documentation so that the developers who try to debug something five years from now can figure out functionality a little -easier! +easier. The protocol specification itself should be freely available without requiring a non-disclosure agreement or similar. diff --git a/docs/libcurl/curl_easy_getinfo.md b/docs/libcurl/curl_easy_getinfo.md index 7157cd21131bd5..31efc316560cbd 100644 --- a/docs/libcurl/curl_easy_getinfo.md +++ b/docs/libcurl/curl_easy_getinfo.md @@ -112,6 +112,11 @@ curl_easy_header(3) instead. See CURLINFO_CONTENT_TYPE(3) List of all known cookies. See CURLINFO_COOKIELIST(3) +## CURLINFO_EARLYDATA_SENT_T + +Amount of TLS early data sent (in number of bytes) when +CURLSSLOPT_EARLYDATA is enabled. + ## CURLINFO_EFFECTIVE_METHOD Last used HTTP method. See CURLINFO_EFFECTIVE_METHOD(3) @@ -346,7 +351,7 @@ In microseconds. See CURLINFO_STARTTRANSFER_TIME_T(3) ## CURLINFO_TLS_SESSION (**Deprecated**) TLS session info that can be used for further processing. See -CURLINFO_TLS_SESSION(3). Use CURLINFO_TLS_SSL_PTR(3) instead! +CURLINFO_TLS_SESSION(3). Use CURLINFO_TLS_SSL_PTR(3) instead. ## CURLINFO_TLS_SSL_PTR diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index e9df2469b54938..e0fe95cacf1dd9 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -37,7 +37,7 @@ appropriate options, the application can change libcurl's behavior. All options are set with an *option* followed by a *parameter*. That parameter can be a **long**, a **function pointer**, an **object pointer** or a **curl_off_t**, depending on what the specific option expects. Read this -manual carefully as bad input values may cause libcurl to behave badly! You +manual carefully as bad input values may cause libcurl to behave badly. You can only set one option in each function call. A typical application uses many curl_easy_setopt(3) calls in the setup phase. diff --git a/docs/libcurl/curl_easy_unescape.md b/docs/libcurl/curl_easy_unescape.md index b6b25993c57d27..2e78ad5d9f074a 100644 --- a/docs/libcurl/curl_easy_unescape.md +++ b/docs/libcurl/curl_easy_unescape.md @@ -59,7 +59,7 @@ int main(void) int decodelen; char *decoded = curl_easy_unescape(curl, "%63%75%72%6c", 12, &decodelen); if(decoded) { - /* do not assume printf() works on the decoded data! */ + /* do not assume printf() works on the decoded data */ printf("Decoded: "); /* ... */ curl_free(decoded); diff --git a/docs/libcurl/curl_escape.md b/docs/libcurl/curl_escape.md index 6dce68ec6d3c29..c24d1890d49316 100644 --- a/docs/libcurl/curl_escape.md +++ b/docs/libcurl/curl_escape.md @@ -26,7 +26,7 @@ char *curl_escape(const char *string, int length); # DESCRIPTION -Obsolete function. Use curl_easy_escape(3) instead! +Obsolete function. Use curl_easy_escape(3) instead. This function converts the given input **string** to a URL encoded string and return that as a new allocated string. All input characters that are not diff --git a/docs/libcurl/curl_formadd.md b/docs/libcurl/curl_formadd.md index 6e0993bb002734..b0f82f08c6d5bc 100644 --- a/docs/libcurl/curl_formadd.md +++ b/docs/libcurl/curl_formadd.md @@ -105,7 +105,7 @@ you must make sure strlen() on the data pointer returns zero. ## CURLFORM_CONTENTSLENGTH -(This option is deprecated. Use *CURLFORM_CONTENTLEN* instead!) +(This option is deprecated. Use *CURLFORM_CONTENTLEN* instead.) followed by a long giving the length of the contents. Note that for *CURLFORM_STREAM* contents, this option is mandatory. diff --git a/docs/libcurl/curl_formfree.md b/docs/libcurl/curl_formfree.md index e431f3aca58d98..d7b4e303d33c17 100644 --- a/docs/libcurl/curl_formfree.md +++ b/docs/libcurl/curl_formfree.md @@ -27,7 +27,7 @@ void curl_formfree(struct curl_httppost *form); # DESCRIPTION -This function is deprecated. Do not use. See curl_mime_init(3) instead! +This function is deprecated. Do not use. See curl_mime_init(3) instead. curl_formfree() is used to clean up data previously built/appended with curl_formadd(3). This must be called when the data has been used, which diff --git a/docs/libcurl/curl_formget.md b/docs/libcurl/curl_formget.md index 4724bba6204e69..ad3efb9d448e95 100644 --- a/docs/libcurl/curl_formget.md +++ b/docs/libcurl/curl_formget.md @@ -44,7 +44,7 @@ return the buffer length passed to it on success. If the **CURLFORM_STREAM** option is used in the formpost, it prevents curl_formget(3) from working until you have performed the actual HTTP request. -This, because first then does libcurl known which actual read callback to use! +This, because first then does libcurl known which actual read callback to use. # %PROTOCOLS% diff --git a/docs/libcurl/curl_free.md b/docs/libcurl/curl_free.md index f59e1218fc5a47..c10ca28034c49a 100644 --- a/docs/libcurl/curl_free.md +++ b/docs/libcurl/curl_free.md @@ -42,7 +42,7 @@ int main(void) { char *width = curl_getenv("COLUMNS"); if(width) { - /* it was set! */ + /* it was set */ curl_free(width); } } diff --git a/docs/libcurl/curl_getenv.md b/docs/libcurl/curl_getenv.md index 4f62161f44c6db..2cfb58148ce73d 100644 --- a/docs/libcurl/curl_getenv.md +++ b/docs/libcurl/curl_getenv.md @@ -40,7 +40,7 @@ int main(void) { char *width = curl_getenv("COLUMNS"); if(width) { - /* it was set! */ + /* it was set */ curl_free(width); } } diff --git a/docs/libcurl/curl_global_init_mem.md b/docs/libcurl/curl_global_init_mem.md index 6f49abfad7e39f..4ac22fbeb8b645 100644 --- a/docs/libcurl/curl_global_init_mem.md +++ b/docs/libcurl/curl_global_init_mem.md @@ -69,7 +69,7 @@ to that man page for documentation. # CAUTION Manipulating these gives considerable powers to the application to severely -screw things up for libcurl. Take care! +screw things up for libcurl. Take care. # %PROTOCOLS% diff --git a/docs/libcurl/curl_mprintf.md b/docs/libcurl/curl_mprintf.md index ef88e4ed2b58f9..8f1daa8c2646e1 100644 --- a/docs/libcurl/curl_mprintf.md +++ b/docs/libcurl/curl_mprintf.md @@ -185,7 +185,7 @@ pointer to a long argument (ell-ell). A following integer conversion corresponds to a *long long* or *unsigned long long* argument, or a following n conversion corresponds to -a pointer to a long long argument. +a pointer to a *long long* argument. ## q diff --git a/docs/libcurl/curl_multi_perform.md b/docs/libcurl/curl_multi_perform.md index 9b854d146568d7..c30ad2116f3163 100644 --- a/docs/libcurl/curl_multi_perform.md +++ b/docs/libcurl/curl_multi_perform.md @@ -69,7 +69,7 @@ removing all the handles and adding new ones. int main(void) { int still_running; - CURL *multi = curl_multi_init(); + CURLM *multi = curl_multi_init(); CURL *curl = curl_easy_init(); if(curl) { curl_multi_add_handle(multi, curl); @@ -85,7 +85,7 @@ int main(void) break; } - /* if there are still transfers, loop! */ + /* if there are still transfers, loop */ } while(still_running); } } diff --git a/docs/libcurl/curl_multi_socket.md b/docs/libcurl/curl_multi_socket.md index 53fe2f7ee2840f..490af19c4f356d 100644 --- a/docs/libcurl/curl_multi_socket.md +++ b/docs/libcurl/curl_multi_socket.md @@ -29,8 +29,8 @@ CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t sockfd, # DESCRIPTION -This function is deprecated. Do not use. See curl_multi_socket_action(3) -instead. +This function is deprecated. Use curl_multi_socket_action(3) instead with +**ev_bitmask** set to 0. At return, the integer **running_handles** points to contains the number of still running easy handles within the multi handle. When this number reaches @@ -54,9 +54,6 @@ to CURL_SOCKET_TIMEOUT. You can also use the curl_multi_timeout(3) function to poll the value at any given time, but for an event-based system using the callback is far better than relying on polling the timeout value. -Usage of curl_multi_socket(3) is deprecated, whereas the function is -equivalent to curl_multi_socket_action(3) with **ev_bitmask** set to 0. - # %PROTOCOLS% # EXAMPLE @@ -76,7 +73,7 @@ int main(void) # DEPRECATED -curl_multi_socket(3) is deprecated, use curl_multi_socket_action(3) instead! +curl_multi_socket(3) is deprecated, use curl_multi_socket_action(3) instead. # %AVAILABILITY% diff --git a/docs/libcurl/curl_multi_socket_all.md b/docs/libcurl/curl_multi_socket_all.md index 37a605bde3bdff..d7f832b51dfa3e 100644 --- a/docs/libcurl/curl_multi_socket_all.md +++ b/docs/libcurl/curl_multi_socket_all.md @@ -30,8 +30,8 @@ CURLMcode curl_multi_socket_all(CURLM *multi_handle, # DESCRIPTION -This function is deprecated. Do not use. See curl_multi_socket_action(3) -instead. +This function is deprecated for performance reasons but there are no plans to +remove it from the API. Use curl_multi_socket_action(3) instead. At return, the integer **running_handles** points to contains the number of still running easy handles within the multi handle. When this number reaches diff --git a/docs/libcurl/curl_url_cleanup.md b/docs/libcurl/curl_url_cleanup.md index 80eff3590fa24f..36455fc0be729b 100644 --- a/docs/libcurl/curl_url_cleanup.md +++ b/docs/libcurl/curl_url_cleanup.md @@ -29,7 +29,7 @@ void curl_url_cleanup(CURLU *handle); # DESCRIPTION -Frees all the resources associated with the given *CURLU* handle! +Frees all the resources associated with the given *CURLU* handle. Passing in a NULL pointer in *handle* makes this function return immediately with no action. diff --git a/docs/libcurl/curl_url_dup.md b/docs/libcurl/curl_url_dup.md index 5c0b4b0210bfa5..28a661a473ce82 100644 --- a/docs/libcurl/curl_url_dup.md +++ b/docs/libcurl/curl_url_dup.md @@ -45,7 +45,7 @@ int main(void) CURLU *url2; rc = curl_url_set(url, CURLUPART_URL, "https://example.com", 0); if(!rc) { - url2 = curl_url_dup(url); /* clone it! */ + url2 = curl_url_dup(url); /* clone it */ curl_url_cleanup(url2); } curl_url_cleanup(url); diff --git a/docs/libcurl/curl_version.md b/docs/libcurl/curl_version.md index 012be6ed8771e3..25c5eb9892831f 100644 --- a/docs/libcurl/curl_version.md +++ b/docs/libcurl/curl_version.md @@ -28,7 +28,7 @@ char *curl_version(); Returns a human readable string with the version number of libcurl and some of its important components (like OpenSSL version). -We recommend using curl_version_info(3) instead! +We recommend using curl_version_info(3) instead. # %PROTOCOLS% diff --git a/docs/libcurl/curl_ws_recv.md b/docs/libcurl/curl_ws_recv.md index d5df8b3391fab0..c13d74a5c68e77 100644 --- a/docs/libcurl/curl_ws_recv.md +++ b/docs/libcurl/curl_ws_recv.md @@ -37,12 +37,14 @@ Retrieves as much as possible of a received WebSocket data fragment into the number of bytes actually stored. If there is more fragment data to deliver than what fits in the provided -*buffer*, libcurl returns a full buffer and the application needs to call -this function again to continue draining the buffer. - -The *meta* pointer gets set to point to a *const struct curl_ws_frame* -that contains information about the received data. See the -curl_ws_meta(3) for details on that struct. +*buffer*, libcurl returns a full buffer and the application needs to call this +function again to continue draining the buffer. + +If the function call is successful, the *meta* pointer gets set to point to a +*const struct curl_ws_frame* that contains information about the received +data. That struct must not be freed and its contents must not be relied upon +anymore once another WebSocket function is called. See the curl_ws_meta(3) for +details on that struct.a # %PROTOCOLS% diff --git a/docs/libcurl/libcurl-errors.md b/docs/libcurl/libcurl-errors.md index e994bd497b8682..110702339d132c 100644 --- a/docs/libcurl/libcurl-errors.md +++ b/docs/libcurl/libcurl-errors.md @@ -60,7 +60,7 @@ The URL was not properly formatted. ## CURLE_NOT_BUILT_IN (4) -A requested feature, protocol or option was not found built-in in this libcurl +A requested feature, protocol or option was not found built into this libcurl due to a build-time decision. This means that a feature or option was not enabled or explicitly disabled when libcurl was built and in order to get it to function you have to get a rebuilt libcurl. @@ -203,9 +203,9 @@ Not used in modern versions. The server does not support or accept range requests. -## CURLE_HTTP_POST_ERROR (34) +## Obsolete error (34) -This is an odd error that mainly occurs due to internal confusion. +Not used since 7.56.0. ## CURLE_SSL_CONNECT_ERROR (35) @@ -236,9 +236,9 @@ LDAP search failed. Not used in modern versions. -## CURLE_FUNCTION_NOT_FOUND (41) +## Obsolete error (41) -Function not found. A required zlib function was not found. +Not used since 7.53.0. ## CURLE_ABORTED_BY_CALLBACK (42) @@ -525,7 +525,7 @@ You are doomed. ## CURLM_INTERNAL_ERROR (4) -This can only be returned if libcurl bugs. Please report it to us! +This can only be returned if libcurl bugs. Please report it to us. ## CURLM_BAD_SOCKET (5) diff --git a/docs/libcurl/libcurl-security.md b/docs/libcurl/libcurl-security.md index 466506bf961db2..e1301698bcf2bd 100644 --- a/docs/libcurl/libcurl-security.md +++ b/docs/libcurl/libcurl-security.md @@ -460,7 +460,7 @@ created. libcurl itself uses *fork()* and *execl()* if told to use the **CURLAUTH_NTLM_WB** authentication method which then invokes the helper command in a child process with file descriptors duplicated. Make sure that -only the trusted and reliable helper program is invoked! +only the trusted and reliable helper program is invoked. This feature was removed from curl in 8.8.0. diff --git a/docs/libcurl/libcurl-share.md b/docs/libcurl/libcurl-share.md index 6d00c9c674f294..040059313d6b68 100644 --- a/docs/libcurl/libcurl-share.md +++ b/docs/libcurl/libcurl-share.md @@ -36,7 +36,7 @@ The share interface was added to enable sharing of data between curl handles. You can have multiple easy handles share data between them. Have them update and use the **same** cookie database, DNS cache, TLS session cache and/or -connection cache! This way, each single transfer takes advantage from data +connection cache. This way, each single transfer takes advantage from data updates made by the other transfer(s). # SHARE OBJECT diff --git a/docs/libcurl/libcurl-url.md b/docs/libcurl/libcurl-url.md index 1d9e592470584b..82de7e7821fd0d 100644 --- a/docs/libcurl/libcurl-url.md +++ b/docs/libcurl/libcurl-url.md @@ -104,7 +104,7 @@ Extracted parts are not URL decoded unless the user also asks for it with the *CURLU_URLDECODE* flag set in the fourth bitmask argument. Remember to free the returned string with curl_free(3) when you are done -with it! +with it. # SET PARTS diff --git a/docs/libcurl/libcurl.md b/docs/libcurl/libcurl.md index 5625aa07d24e74..b6d4af36e35d75 100644 --- a/docs/libcurl/libcurl.md +++ b/docs/libcurl/libcurl.md @@ -119,7 +119,7 @@ a lowercase c). You can find other functions in the library source code, but other prefixes indicate that the functions are private and may change without further notice in the next release. -Only use documented functions and functionality! +Only use documented functions and functionality. # PORTABILITY diff --git a/docs/libcurl/opts/CURLINFO_EARLYDATA_SENT_T.md b/docs/libcurl/opts/CURLINFO_EARLYDATA_SENT_T.md new file mode 100644 index 00000000000000..427746077ea95e --- /dev/null +++ b/docs/libcurl/opts/CURLINFO_EARLYDATA_SENT_T.md @@ -0,0 +1,75 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLINFO_EARLYDATA_SENT_T +Section: 3 +Source: libcurl +See-also: + - curl_easy_getinfo (3) + - curl_easy_setopt (3) +Protocol: + - TLS +TLS-backend: + - GnuTLS +Added-in: 8.11.0 +--- + +# NAME + +CURLINFO_EARLYDATA_SENT_T - get the number of bytes sent as TLS early data + +# SYNOPSIS + +~~~c +#include + +CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_EARLYDATA_SENT_T, + curl_off_t *amount); +~~~ + +# DESCRIPTION + +Pass a pointer to an *curl_off_t* to receive the total amount of bytes that +were sent to the server as TLSv1.3 early data. When no TLS early +data is used, this reports 0. + +TLS early data is only attempted when CURLSSLOPT_EARLYDATA is set for the +transfer. In addition, it is only used by libcurl when a TLS session exists +that announces support. + +The amount is **negative** when the sent data was rejected +by the server. TLS allows a server that announces support for early data to +reject any attempt to use it at its own discretion. When for example 127 +bytes had been sent, but were rejected, it reports -127 as the amount "sent". + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURL *curl = curl_easy_init(); + if(curl) { + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); + + /* Perform the request */ + res = curl_easy_perform(curl); + + if(!res) { + curl_off_t amount; + res = curl_easy_getinfo(curl, CURLINFO_EARLYDATA_SENT_T, &amount); + if(!res) { + printf("TLS earlydata: %" CURL_FORMAT_CURL_OFF_T " bytes\n", amount); + } + } + } +} +~~~ + +# %AVAILABILITY% + +# RETURN VALUE + +Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not. diff --git a/docs/libcurl/opts/CURLINFO_LASTSOCKET.md b/docs/libcurl/opts/CURLINFO_LASTSOCKET.md index 5a71886247fbbe..957f21f4b8b8fd 100644 --- a/docs/libcurl/opts/CURLINFO_LASTSOCKET.md +++ b/docs/libcurl/opts/CURLINFO_LASTSOCKET.md @@ -51,7 +51,7 @@ int main(void) CURL *curl = curl_easy_init(); if(curl) { CURLcode res; - long sockfd; /* does not work on win64! */ + long sockfd; /* does not work on win64 */ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); /* Do not do the transfer - only connect to host */ diff --git a/docs/libcurl/opts/CURLINFO_PROTOCOL.md b/docs/libcurl/opts/CURLINFO_PROTOCOL.md index 9dadae91c725f8..6e466f77764ead 100644 --- a/docs/libcurl/opts/CURLINFO_PROTOCOL.md +++ b/docs/libcurl/opts/CURLINFO_PROTOCOL.md @@ -29,7 +29,7 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_PROTOCOL, long *p); This option is deprecated. We strongly recommend using CURLINFO_SCHEME(3) instead, because this option cannot return all -possible protocols! +possible protocols. Pass a pointer to a long to receive the version used in the last http connection. The returned value is set to one of the CURLPROTO_* values: diff --git a/docs/libcurl/opts/CURLINFO_RTSP_SERVER_CSEQ.md b/docs/libcurl/opts/CURLINFO_RTSP_SERVER_CSEQ.md index 1b1d73d88d22f1..ee306e8e6b95ea 100644 --- a/docs/libcurl/opts/CURLINFO_RTSP_SERVER_CSEQ.md +++ b/docs/libcurl/opts/CURLINFO_RTSP_SERVER_CSEQ.md @@ -31,7 +31,7 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_RTSP_SERVER_CSEQ, Pass a pointer to a long to receive the next CSeq that is expected to be used by the application. -Listening for server initiated requests is not implemented! +Listening for server initiated requests is not implemented. Applications wishing to resume an RTSP session on another connection should retrieve this info before closing the active connection. diff --git a/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md b/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md index d4223f1265f179..9e9a55ff075a34 100644 --- a/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md +++ b/docs/libcurl/opts/CURLMOPT_MAXCONNECTS.md @@ -26,17 +26,16 @@ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_MAXCONNECTS, long max); # DESCRIPTION -Pass a long indicating the **max**. The set number is used as the maximum -amount of simultaneously open connections that libcurl may keep in its -connection cache after completed use. By default libcurl enlarges the size for -each added easy handle to make it fit 4 times the number of added easy -handles. +Pass a long indicating the **max**, the maximum amount of connections that +libcurl may keep alive in its connection cache after use. By default libcurl +enlarges the size for each added easy handle to make it fit 4 times the number +of added easy handles. -By setting this option, you can prevent the cache size from growing beyond the +By setting this option, you prevent the cache size from growing beyond the limit set by you. -When the cache is full, curl closes the oldest one in the cache to prevent the -number of open connections from increasing. +When the cache is full, curl closes the oldest connection present in the cache +to prevent the number of connections from increasing. This option is for the multi handle's use only, when using the easy interface you should instead use the CURLOPT_MAXCONNECTS(3) option. @@ -46,8 +45,8 @@ connections. Changing this value when there are transfers in progress is possible, and the new value is then used the next time checks are performed. Lowering the value -does however not close down any active transfers, it simply does not allow new -ones to get made. +does not close down any active transfers, it simply does not allow new ones to +get made. # DEFAULT diff --git a/docs/libcurl/opts/CURLMOPT_MAX_HOST_CONNECTIONS.md b/docs/libcurl/opts/CURLMOPT_MAX_HOST_CONNECTIONS.md index 39567f1e20b3c0..07fa6bf6df3c6e 100644 --- a/docs/libcurl/opts/CURLMOPT_MAX_HOST_CONNECTIONS.md +++ b/docs/libcurl/opts/CURLMOPT_MAX_HOST_CONNECTIONS.md @@ -27,30 +27,31 @@ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_MAX_HOST_CONNECTIONS, # DESCRIPTION -Pass a long to indicate **max**. The set number is used as the maximum amount -of simultaneously open connections to a single host (a host being the same as -a hostname + port number pair). For each new session to a host, libcurl might -open a new connection up to the limit set by CURLMOPT_MAX_HOST_CONNECTIONS(3). -When the limit is reached, new sessions are kept pending until a connection -becomes available. +Pass a long to indicate **max**, the maximum amount of simultaneously open +connections libcurl may hold a single host (a host being the same as a +hostname + port number pair). For each new transfer to the same host, libcurl +might open a new connection up to the limit set by +CURLMOPT_MAX_HOST_CONNECTIONS(3). When the limit is reached, new sessions are +kept pending until a connection becomes available. The default **max** value is 0, unlimited. This set limit is also used for proxy connections, and then the proxy is considered to be the host for which this limit counts. When more transfers are added to the multi handle than what can be performed -due to the set limit, they are queued up waiting for their chance. When that -happens, the CURLOPT_TIMEOUT_MS(3) timeout is inclusive of the waiting time, -meaning that if you set a too narrow timeout in such a case the transfer might -never even start before it times out. - -Even in the queued up situation, the CURLOPT_CONNECTTIMEOUT_MS(3) timeout is -however treated as a per-connect timeout. - -Changing this value when there are transfers in progress is possible, and the -new value is then used the next time checks are performed. Lowering the value -does however not close down any active transfers, it simply does not allow new -ones to get made. +due to the set limit, they are queued up waiting for their chance. + +While a transfer is queued up internally waiting for a connection, the +CURLOPT_TIMEOUT_MS(3) timeout is counted inclusive of the waiting time, +meaning that if you set a too narrow timeout the transfer might never even +start before it times out. The CURLOPT_CONNECTTIMEOUT_MS(3) time is also +similarly still treated as a per-connect timeout and might expire even before +making a new connection is permitted. + +Changing this value while there are transfers in progress is possible. The new +value is then used the next time checks are performed. Lowering the value does +not close down any active transfers, it simply does not allow new ones to get +made. # DEFAULT diff --git a/docs/libcurl/opts/CURLMOPT_MAX_TOTAL_CONNECTIONS.md b/docs/libcurl/opts/CURLMOPT_MAX_TOTAL_CONNECTIONS.md index 1a7fe170fe77e9..b014453a8a9b05 100644 --- a/docs/libcurl/opts/CURLMOPT_MAX_TOTAL_CONNECTIONS.md +++ b/docs/libcurl/opts/CURLMOPT_MAX_TOTAL_CONNECTIONS.md @@ -30,24 +30,29 @@ CURLMcode curl_multi_setopt(CURLM *handle, CURLMOPT_MAX_TOTAL_CONNECTIONS, Pass a long for the **amount**. The set number is used as the maximum number of simultaneously open connections in total using this multi handle. For each new session, libcurl might open a new connection up to the limit set by -CURLMOPT_MAX_TOTAL_CONNECTIONS(3). When the limit is reached, new -sessions are held pending until there are available connections. If -CURLMOPT_PIPELINING(3) is enabled, libcurl can try multiplexing if the -host is capable of it. +CURLMOPT_MAX_TOTAL_CONNECTIONS(3). If CURLMOPT_PIPELINING(3) is enabled, +libcurl can try multiplexing if the host is capable of it. When more transfers are added to the multi handle than what can be performed -due to the set limit, they get queued up waiting for their chance. When that -happens, the CURLOPT_TIMEOUT_MS(3) timeout is counted inclusive of the -waiting time, meaning that if you set a too narrow timeout in such a case the -transfer might never even start before it times out. +due to the set limit, they get queued up waiting for their chance. -Even in the queued up situation, the CURLOPT_CONNECTTIMEOUT_MS(3) -timeout is however treated as a per-connect timeout. +While a transfer is queued up internally waiting for a connection, the +CURLOPT_TIMEOUT_MS(3) timeout is counted inclusive of the waiting time, +meaning that if you set a too narrow timeout the transfer might never even +start before it times out. The CURLOPT_CONNECTTIMEOUT_MS(3) time is also +similarly still treated as a per-connect timeout and might expire even before +making a new connection is permitted. + +Changing this value while there are transfers in progress is possible. The new +value is then used the next time checks are performed. Lowering the value does +not close down any active transfers, it simply does not allow new ones to get +made. # DEFAULT 0, which means that there is no limit. It is then simply controlled by the -number of easy handles added. +number of easy handles added concurrently and how much multiplexing is being +done. # %PROTOCOLS% diff --git a/docs/libcurl/opts/CURLOPT_APPEND.md b/docs/libcurl/opts/CURLOPT_APPEND.md index 857f88012f9202..867cace74ffeb1 100644 --- a/docs/libcurl/opts/CURLOPT_APPEND.md +++ b/docs/libcurl/opts/CURLOPT_APPEND.md @@ -10,6 +10,7 @@ See-also: - CURLOPT_UPLOAD (3) Protocol: - FTP + - SFTP Added-in: 7.17.0 --- diff --git a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md index 5882a9e25cdae6..7d9af2ea3d2f7c 100644 --- a/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_CHUNK_BGN_FUNCTION.md @@ -24,7 +24,7 @@ CURLOPT_CHUNK_BGN_FUNCTION - callback before a transfer with FTP wildcard match struct curl_fileinfo { char *filename; curlfiletype filetype; - time_t time; /* always zero! */ + time_t time; /* always zero */ unsigned int perm; int uid; int gid; diff --git a/docs/libcurl/opts/CURLOPT_CONNECT_ONLY.md b/docs/libcurl/opts/CURLOPT_CONNECT_ONLY.md index f5a87f00a2c871..7b2e713afe3bd6 100644 --- a/docs/libcurl/opts/CURLOPT_CONNECT_ONLY.md +++ b/docs/libcurl/opts/CURLOPT_CONNECT_ONLY.md @@ -69,7 +69,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); ret = curl_easy_perform(curl); if(ret == CURLE_OK) { - /* only connected! */ + /* only connected */ } } } diff --git a/docs/libcurl/opts/CURLOPT_COOKIEJAR.md b/docs/libcurl/opts/CURLOPT_COOKIEJAR.md index 3f76eb7e690c75..f9c17e8538ae84 100644 --- a/docs/libcurl/opts/CURLOPT_COOKIEJAR.md +++ b/docs/libcurl/opts/CURLOPT_COOKIEJAR.md @@ -73,7 +73,7 @@ int main(void) res = curl_easy_perform(curl); - /* close the handle, write the cookies! */ + /* close the handle, write the cookies */ curl_easy_cleanup(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_DNS_USE_GLOBAL_CACHE.md b/docs/libcurl/opts/CURLOPT_DNS_USE_GLOBAL_CACHE.md index f236c3885a965a..20670db00acc31 100644 --- a/docs/libcurl/opts/CURLOPT_DNS_USE_GLOBAL_CACHE.md +++ b/docs/libcurl/opts/CURLOPT_DNS_USE_GLOBAL_CACHE.md @@ -27,7 +27,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_DNS_USE_GLOBAL_CACHE, # DESCRIPTION -Has no function since 7.62.0. Do not use! +Has no function since 7.62.0. Do not use. Pass a long. If the *enable* value is 1, it tells curl to use a global DNS cache that survives between easy handle creations and deletions. This is not diff --git a/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md b/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md index bcde0e1065ff0d..95227616c5ff4b 100644 --- a/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md +++ b/docs/libcurl/opts/CURLOPT_DOH_SSL_VERIFYHOST.md @@ -51,7 +51,7 @@ for consistency with the other *VERIFYHOST* options we suggest use 2 and not 1. When the *verify* value is set to 0L, the connection succeeds regardless of -the names used in the certificate. Use that ability with caution! +the names used in the certificate. Use that ability with caution. See also CURLOPT_DOH_SSL_VERIFYPEER(3) to verify the digital signature of the DoH server certificate. diff --git a/docs/libcurl/opts/CURLOPT_FTP_CREATE_MISSING_DIRS.md b/docs/libcurl/opts/CURLOPT_FTP_CREATE_MISSING_DIRS.md index 7601278e465b2c..f428243ca00b8f 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_CREATE_MISSING_DIRS.md +++ b/docs/libcurl/opts/CURLOPT_FTP_CREATE_MISSING_DIRS.md @@ -50,7 +50,7 @@ retry the CWD command again if the subsequent **MKD** command fails. This is especially useful if you are doing many simultaneous connections against the same server and they all have this option enabled, as then CWD may first fail but then another connection does **MKD** before this connection and thus -**MKD** fails but trying CWD works! +**MKD** fails but trying CWD works. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_FTP_USE_PRET.md b/docs/libcurl/opts/CURLOPT_FTP_USE_PRET.md index 1e078581f6b0dc..d50be758a143d4 100644 --- a/docs/libcurl/opts/CURLOPT_FTP_USE_PRET.md +++ b/docs/libcurl/opts/CURLOPT_FTP_USE_PRET.md @@ -48,7 +48,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "ftp://example.com/old-server/file.txt"); - /* a drftpd server, do it! */ + /* a drftpd server, do it */ curl_easy_setopt(curl, CURLOPT_FTP_USE_PRET, 1L); res = curl_easy_perform(curl); diff --git a/docs/libcurl/opts/CURLOPT_HEADER.md b/docs/libcurl/opts/CURLOPT_HEADER.md index 348f4d99a00ede..fd81b37c810809 100644 --- a/docs/libcurl/opts/CURLOPT_HEADER.md +++ b/docs/libcurl/opts/CURLOPT_HEADER.md @@ -48,7 +48,7 @@ It is often better to use CURLOPT_HEADERFUNCTION(3) to get the header data separately. While named confusingly similar, CURLOPT_HTTPHEADER(3) is used to set -custom HTTP headers! +custom HTTP headers. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md b/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md index 688f1f3baffbd9..3c7e0a9e54c2ae 100644 --- a/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_HEADERFUNCTION.md @@ -43,12 +43,12 @@ shown above. This callback function gets invoked by libcurl as soon as it has received header data. The header callback is called once for each header and only complete header lines are passed on to the callback. Parsing headers is easy -to do using this callback. *buffer* points to the delivered data, and the -size of that data is *nitems*; *size* is always 1. The provide header -line is not null-terminated! +to do using this callback. *buffer* points to the delivered data, and the size +of that data is *nitems*; *size* is always 1. The provided header line is not +null-terminated. Do not modify the passed in buffer. -The pointer named *userdata* is the one you set with the -CURLOPT_HEADERDATA(3) option. +The pointer named *userdata* is the one you set with the CURLOPT_HEADERDATA(3) +option. Your callback should return the number of bytes actually taken care of. If that amount differs from the amount passed to your callback function, it diff --git a/docs/libcurl/opts/CURLOPT_HEADEROPT.md b/docs/libcurl/opts/CURLOPT_HEADEROPT.md index bf2a6f6bc0f47e..9c74c6fa53f6e1 100644 --- a/docs/libcurl/opts/CURLOPT_HEADEROPT.md +++ b/docs/libcurl/opts/CURLOPT_HEADEROPT.md @@ -64,7 +64,7 @@ int main(void) /* HTTPS over a proxy makes a separate CONNECT to the proxy, so tell libcurl to not send the custom headers to the proxy. Keep them - separate! */ + separate. */ curl_easy_setopt(curl, CURLOPT_HEADEROPT, CURLHEADER_SEPARATE); ret = curl_easy_perform(curl); curl_slist_free_all(list); diff --git a/docs/libcurl/opts/CURLOPT_HTTP09_ALLOWED.md b/docs/libcurl/opts/CURLOPT_HTTP09_ALLOWED.md index 28fbe0a9137855..56c177cfae4e25 100644 --- a/docs/libcurl/opts/CURLOPT_HTTP09_ALLOWED.md +++ b/docs/libcurl/opts/CURLOPT_HTTP09_ALLOWED.md @@ -30,7 +30,7 @@ Pass the long argument *allowed* set to 1L to allow HTTP/0.9 responses. An HTTP/0.9 response is a server response entirely without headers and only a body. You can connect to lots of random TCP services and still get a response -that curl might consider to be HTTP/0.9! +that curl might consider to be HTTP/0.9. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_HTTPHEADER.md b/docs/libcurl/opts/CURLOPT_HTTPHEADER.md index d3c06457b6b3b7..cd49b1b89ef2ac 100644 --- a/docs/libcurl/opts/CURLOPT_HTTPHEADER.md +++ b/docs/libcurl/opts/CURLOPT_HTTPHEADER.md @@ -35,7 +35,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTPHEADER, Pass a pointer to a linked list of HTTP headers to pass to the server and/or proxy in your HTTP request. The same list can be used for both host and proxy -requests! +requests. When used within an IMAP or SMTP request to upload a MIME mail, the given header list establishes the document-level MIME headers to prepend to the diff --git a/docs/libcurl/opts/CURLOPT_IOCTLFUNCTION.md b/docs/libcurl/opts/CURLOPT_IOCTLFUNCTION.md index b26a542f7a8f75..8aa01a9efc3139 100644 --- a/docs/libcurl/opts/CURLOPT_IOCTLFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_IOCTLFUNCTION.md @@ -57,7 +57,7 @@ The *clientp* argument to the callback is set with the CURLOPT_IOCTLDATA(3) option. **This option is deprecated**. Do not use it. Use CURLOPT_SEEKFUNCTION(3) -instead to provide seeking! If CURLOPT_SEEKFUNCTION(3) is set, this +instead to provide seeking. If CURLOPT_SEEKFUNCTION(3) is set, this parameter is ignored when seeking. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_LOW_SPEED_LIMIT.md b/docs/libcurl/opts/CURLOPT_LOW_SPEED_LIMIT.md index 58415df12ef254..068c6572cd53b3 100644 --- a/docs/libcurl/opts/CURLOPT_LOW_SPEED_LIMIT.md +++ b/docs/libcurl/opts/CURLOPT_LOW_SPEED_LIMIT.md @@ -54,7 +54,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 30L); res = curl_easy_perform(curl); if(CURLE_OPERATION_TIMEDOUT == res) { - printf("Timeout!\n"); + printf("Timeout.\n"); } /* always cleanup */ curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_LOW_SPEED_TIME.md b/docs/libcurl/opts/CURLOPT_LOW_SPEED_TIME.md index 04edef33461b7e..819e7c1ccade65 100644 --- a/docs/libcurl/opts/CURLOPT_LOW_SPEED_TIME.md +++ b/docs/libcurl/opts/CURLOPT_LOW_SPEED_TIME.md @@ -51,7 +51,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 30L); res = curl_easy_perform(curl); if(CURLE_OPERATION_TIMEDOUT == res) { - printf("Timeout!\n"); + printf("Timeout.\n"); } /* always cleanup */ curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_MAXCONNECTS.md b/docs/libcurl/opts/CURLOPT_MAXCONNECTS.md index e1e0c2565ea692..3a368c8254ed2b 100644 --- a/docs/libcurl/opts/CURLOPT_MAXCONNECTS.md +++ b/docs/libcurl/opts/CURLOPT_MAXCONNECTS.md @@ -28,18 +28,18 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_MAXCONNECTS, long amount); # DESCRIPTION -Pass a long. The set *amount* is the maximum number of simultaneously open -persistent connections that libcurl may cache in the pool associated with this -handle. The default is 5, and there is not much point in changing this value -unless you are perfectly aware of how this works. This concerns connections -using any of the protocols that support persistent connections. +Pass a long. The set *amount* is the maximum number of connections that +libcurl may keep alive in its connection cache after use. The default is 5, +and there is not much point in changing this value unless you are perfectly +aware of how this works. This concerns connections using any of the protocols +that support persistent connections. -When reaching the maximum limit, curl closes the oldest one in the cache to -prevent increasing the number of open connections. +When reaching the maximum limit, curl closes the oldest connection present in +the cache to prevent the number of connections from increasing. If you already have performed transfers with this curl handle, setting a -smaller CURLOPT_MAXCONNECTS(3) than before may cause open connections to -get closed unnecessarily. +smaller CURLOPT_MAXCONNECTS(3) than before may cause open connections to get +closed unnecessarily. If you add this easy handle to a multi handle, this setting is not acknowledged, and you must instead use curl_multi_setopt(3) and the diff --git a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md index a90763012824fd..d28f3dc0ef6f16 100644 --- a/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md +++ b/docs/libcurl/opts/CURLOPT_MAXFILESIZE.md @@ -56,7 +56,7 @@ int main(void) if(curl) { CURLcode ret; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - /* refuse to download if larger than 1000 bytes! */ + /* refuse to download if larger than 1000 bytes */ curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, 1000L); ret = curl_easy_perform(curl); } diff --git a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md index 22f3cb065890cc..4619f272eef786 100644 --- a/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md +++ b/docs/libcurl/opts/CURLOPT_MAX_SEND_SPEED_LARGE.md @@ -58,7 +58,7 @@ int main(void) curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* cap the upload speed to 1000 bytes/sec */ curl_easy_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, (curl_off_t)1000); - /* (set some upload options as well!) */ + /* (set some upload options as well) */ ret = curl_easy_perform(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_MIMEPOST.md b/docs/libcurl/opts/CURLOPT_MIMEPOST.md index 9e3b3806c2e34b..4cd3238bf69e0c 100644 --- a/docs/libcurl/opts/CURLOPT_MIMEPOST.md +++ b/docs/libcurl/opts/CURLOPT_MIMEPOST.md @@ -68,7 +68,7 @@ int main(void) /* Set the form info */ curl_easy_setopt(curl, CURLOPT_MIMEPOST, multipart); - curl_easy_perform(curl); /* post away! */ + curl_easy_perform(curl); /* post away */ curl_mime_free(multipart); /* free the post data */ } } diff --git a/docs/libcurl/opts/CURLOPT_NOBODY.md b/docs/libcurl/opts/CURLOPT_NOBODY.md index 0168912add888a..e1643fb1b4b81d 100644 --- a/docs/libcurl/opts/CURLOPT_NOBODY.md +++ b/docs/libcurl/opts/CURLOPT_NOBODY.md @@ -61,7 +61,7 @@ int main(void) if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); - /* get us the resource without a body - use HEAD! */ + /* get us the resource without a body - use HEAD */ curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); /* Perform the request */ diff --git a/docs/libcurl/opts/CURLOPT_PATH_AS_IS.md b/docs/libcurl/opts/CURLOPT_PATH_AS_IS.md index 744de5c3fdbd4c..ef78004320e562 100644 --- a/docs/libcurl/opts/CURLOPT_PATH_AS_IS.md +++ b/docs/libcurl/opts/CURLOPT_PATH_AS_IS.md @@ -16,7 +16,7 @@ Added-in: 7.42.0 # NAME -CURLOPT_PATH_AS_IS - do not handle dot dot sequences +CURLOPT_PATH_AS_IS - do not handle dot-dot sequences # SYNOPSIS @@ -35,7 +35,7 @@ This instructs libcurl to NOT squash sequences of "/../" or "/./" that may exist in the URL's path part and that is supposed to be removed according to RFC 3986 section 5.2.4. -Some server implementations are known to (erroneously) require the dot dot +Some server implementations are known to (erroneously) require the dot-dot sequences to remain in the path and some clients want to pass these on in order to try out server implementations. diff --git a/docs/libcurl/opts/CURLOPT_PREREQFUNCTION.md b/docs/libcurl/opts/CURLOPT_PREREQFUNCTION.md index 8024a9a897e47a..bb9bb822e98e0f 100644 --- a/docs/libcurl/opts/CURLOPT_PREREQFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_PREREQFUNCTION.md @@ -51,7 +51,8 @@ This function may be called multiple times if redirections are enabled and are being followed (see CURLOPT_FOLLOWLOCATION(3)). The callback function must return *CURL_PREREQFUNC_OK* on success, or -*CURL_PREREQFUNC_ABORT* to cause the transfer to fail. +*CURL_PREREQFUNC_ABORT* to cause the transfer to fail with result +*CURLE_ABORTED_BY_CALLBACK*. This function is passed the following arguments: diff --git a/docs/libcurl/opts/CURLOPT_PROTOCOLS.md b/docs/libcurl/opts/CURLOPT_PROTOCOLS.md index e631bca9ddb03b..346d9f58ff2cb7 100644 --- a/docs/libcurl/opts/CURLOPT_PROTOCOLS.md +++ b/docs/libcurl/opts/CURLOPT_PROTOCOLS.md @@ -29,7 +29,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_PROTOCOLS, long bitmask); This option is deprecated. We strongly recommend using CURLOPT_PROTOCOLS_STR(3) instead because this option cannot control all -available protocols! +available protocols. Pass a long that holds a bitmask of CURLPROTO_* defines. If used, this bitmask limits what protocols libcurl may use in the transfer. This allows you to have diff --git a/docs/libcurl/opts/CURLOPT_PROXY.md b/docs/libcurl/opts/CURLOPT_PROXY.md index c414f4f5129686..7c845560245260 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY.md +++ b/docs/libcurl/opts/CURLOPT_PROXY.md @@ -102,6 +102,10 @@ single port number used widely for proxies. Specify it. When a proxy is used, the active FTP mode as set with *CUROPT_FTPPORT(3)*, cannot be used. +Doing FTP over an HTTP proxy without CURLOPT_HTTPPROXYTUNNEL(3) set makes +libcurl do HTTP with an FTP URL over the proxy. For such transfers, common FTP +specific options do not work, for example CURLOPT_USE_SSL(3). + # Environment variables libcurl respects the proxy environment variables named **http_proxy**, diff --git a/docs/libcurl/opts/CURLOPT_PROXY_SSL_VERIFYHOST.md b/docs/libcurl/opts/CURLOPT_PROXY_SSL_VERIFYHOST.md index d97cd74bfa77c2..0d195daf764505 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_SSL_VERIFYHOST.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_SSL_VERIFYHOST.md @@ -56,7 +56,7 @@ an error and leaving the flag untouched. From 7.66.0: treats 1 and 2 the same. When the *verify* value is 0L, the connection succeeds regardless of the -names used in the certificate. Use that ability with caution! +names used in the certificate. Use that ability with caution. See also CURLOPT_PROXY_SSL_VERIFYPEER(3) to verify the digital signature of the proxy certificate. diff --git a/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.md b/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.md index 2294ec69c010c9..ba5d97edbc25bb 100644 --- a/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.md +++ b/docs/libcurl/opts/CURLOPT_PROXY_TLS13_CIPHERS.md @@ -90,7 +90,7 @@ wolfSSL support added in 8.10.0. mbedTLS support added in 8.10.0, available when built with mbedTLS \>= 3.6.0. Rustls support added in 8.10.0. -Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites where set +Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites were set by using the CURLOPT_PROXY_SSL_CIPHER_LIST(3) option. # %AVAILABILITY% diff --git a/docs/libcurl/opts/CURLOPT_READFUNCTION.md b/docs/libcurl/opts/CURLOPT_READFUNCTION.md index 72e9e75bfcb72b..9e3c3d1f85494f 100644 --- a/docs/libcurl/opts/CURLOPT_READFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_READFUNCTION.md @@ -86,7 +86,7 @@ size_t read_callback(char *ptr, size_t size, size_t nmemb, void *userdata) curl_off_t nread; /* copy as much data as possible into the 'ptr' buffer, but no more than - 'size' * 'nmemb' bytes! */ + 'size' * 'nmemb' bytes. */ size_t retcode = fread(ptr, size, nmemb, readhere); nread = (curl_off_t)retcode; diff --git a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md index 278e4dcd013fba..28fd76b102bb90 100644 --- a/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md +++ b/docs/libcurl/opts/CURLOPT_REDIR_PROTOCOLS.md @@ -30,7 +30,7 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_REDIR_PROTOCOLS, long bitmask); This option is deprecated. We strongly recommend using CURLOPT_REDIR_PROTOCOLS_STR(3) instead because this option cannot -control all available protocols! +control all available protocols. Pass a long that holds a bitmask of CURLPROTO_* defines. If used, this bitmask limits what protocols libcurl may use in a transfer that it follows to in a diff --git a/docs/libcurl/opts/CURLOPT_RTSP_REQUEST.md b/docs/libcurl/opts/CURLOPT_RTSP_REQUEST.md index 99a94bce46c98c..4173ce624ebb52 100644 --- a/docs/libcurl/opts/CURLOPT_RTSP_REQUEST.md +++ b/docs/libcurl/opts/CURLOPT_RTSP_REQUEST.md @@ -123,7 +123,7 @@ int main(void) if(curl) { CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "rtsp://example.com/"); - /* ask for options! */ + /* ask for options */ curl_easy_setopt(curl, CURLOPT_RTSP_REQUEST, CURL_RTSPREQ_OPTIONS); res = curl_easy_perform(curl); curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_SEEKFUNCTION.md b/docs/libcurl/opts/CURLOPT_SEEKFUNCTION.md index 120b254d5a38f8..ea8ff5e30ebd31 100644 --- a/docs/libcurl/opts/CURLOPT_SEEKFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SEEKFUNCTION.md @@ -60,7 +60,7 @@ done by instead reading from the input or similar. If you forward the input arguments directly to fseek(3) or lseek(3), note that the data type for *offset* is not the same as defined for curl_off_t on -many systems! +many systems. # DEFAULT diff --git a/docs/libcurl/opts/CURLOPT_SSH_KEYFUNCTION.md b/docs/libcurl/opts/CURLOPT_SSH_KEYFUNCTION.md index bf7f03710613c7..3d815abdb7c0e6 100644 --- a/docs/libcurl/opts/CURLOPT_SSH_KEYFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_SSH_KEYFUNCTION.md @@ -34,7 +34,7 @@ enum curl_khstat { enum curl_khmatch { CURLKHMATCH_OK, /* match */ - CURLKHMATCH_MISMATCH, /* host found, key mismatch! */ + CURLKHMATCH_MISMATCH, /* host found, key mismatch */ CURLKHMATCH_MISSING, /* no matching host/key found */ }; diff --git a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md index 21e0d75e346695..f789a8b529fb9c 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_OPTIONS.md @@ -86,6 +86,15 @@ certificate that supports client authentication in the OS certificate store it could be a privacy violation and unexpected. (Added in 7.77.0) +## CURLSSLOPT_EARLYDATA + +Tell libcurl to try sending application data as TLS1.3 early data. This option +is only supported for GnuTLS. This option works on a best effort basis, +in cases when it wasn't 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) + # DEFAULT 0 diff --git a/docs/libcurl/opts/CURLOPT_SSL_SESSIONID_CACHE.md b/docs/libcurl/opts/CURLOPT_SSL_SESSIONID_CACHE.md index 1ed5a91f1e94ed..369070269c1379 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_SESSIONID_CACHE.md +++ b/docs/libcurl/opts/CURLOPT_SSL_SESSIONID_CACHE.md @@ -52,7 +52,7 @@ int main(void) if(curl) { CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - /* switch off session-id use! */ + /* switch off session-id use */ curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0L); res = curl_easy_perform(curl); curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.md b/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.md index d9ee57c0d61516..aa8653d1c0c621 100644 --- a/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.md +++ b/docs/libcurl/opts/CURLOPT_SSL_VERIFYSTATUS.md @@ -53,7 +53,7 @@ int main(void) if(curl) { CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); - /* ask for OCSP stapling! */ + /* ask for OCSP stapling */ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L); res = curl_easy_perform(curl); curl_easy_cleanup(curl); diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md index 99664423d53c28..1429de1f80f705 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS.md @@ -64,7 +64,7 @@ int main(void) curl_easy_setopt(curl2, CURLOPT_URL, "https://example.com/two"); curl_easy_setopt(curl2, CURLOPT_STREAM_DEPENDS, curl); - /* then add both to a multi handle and transfer them! */ + /* then add both to a multi handle and transfer them */ } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md index 158c831a111905..bddfead62f3ada 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_DEPENDS_E.md @@ -67,7 +67,7 @@ int main(void) curl_easy_setopt(curl2, CURLOPT_URL, "https://example.com/two"); curl_easy_setopt(curl2, CURLOPT_STREAM_DEPENDS_E, curl); - /* then add both to a multi handle and transfer them! */ + /* then add both to a multi handle and transfer them */ } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_STREAM_WEIGHT.md b/docs/libcurl/opts/CURLOPT_STREAM_WEIGHT.md index 99558a199e1a0c..57088a79067bbd 100644 --- a/docs/libcurl/opts/CURLOPT_STREAM_WEIGHT.md +++ b/docs/libcurl/opts/CURLOPT_STREAM_WEIGHT.md @@ -67,7 +67,7 @@ int main(void) curl_easy_setopt(curl2, CURLOPT_URL, "https://example.com/two"); curl_easy_setopt(curl2, CURLOPT_STREAM_WEIGHT, 20L); - /* then add both to a multi handle and transfer them! */ + /* then add both to a multi handle and transfer them */ } } ~~~ diff --git a/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.md b/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.md index e720db7bf944f7..d2c6226d5c62d4 100644 --- a/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.md +++ b/docs/libcurl/opts/CURLOPT_TLS13_CIPHERS.md @@ -90,7 +90,7 @@ wolfSSL support added in 8.10.0. mbedTLS support added in 8.10.0, available when built with mbedTLS \>= 3.6.0. Rustls support added in 8.10.0. -Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites where set +Before curl 8.10.0 with mbedTLS or wolfSSL, TLS 1.3 cipher suites were set by using the CURLOPT_SSL_CIPHER_LIST(3) option. # %AVAILABILITY% diff --git a/docs/libcurl/opts/CURLOPT_UPLOAD.md b/docs/libcurl/opts/CURLOPT_UPLOAD.md index 4576d27ff6782f..eec0906629cd94 100644 --- a/docs/libcurl/opts/CURLOPT_UPLOAD.md +++ b/docs/libcurl/opts/CURLOPT_UPLOAD.md @@ -83,7 +83,7 @@ int main(void) /* Set the size of the file to upload */ curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fsize); - /* Now run off and do what you have been told! */ + /* Now run off and do what you have been told */ curl_easy_perform(curl); } } diff --git a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md index 022f5da0b04ada..3ee11d8e476ef7 100644 --- a/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md +++ b/docs/libcurl/opts/CURLOPT_WRITEFUNCTION.md @@ -95,7 +95,7 @@ static size_t cb(char *data, size_t size, size_t nmemb, void *clientp) char *ptr = realloc(mem->response, mem->size + realsize + 1); if(!ptr) - return 0; /* out of memory! */ + return 0; /* out of memory */ mem->response = ptr; memcpy(&(mem->response[mem->size]), data, realsize); diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index edabe37a7534d0..8591f47b55da49 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -32,14 +32,15 @@ man_MANS = \ CURLINFO_CERTINFO.3 \ CURLINFO_CONDITION_UNMET.3 \ CURLINFO_CONNECT_TIME.3 \ - CURLINFO_CONNECT_TIME_T.3 \ CURLINFO_CONN_ID.3 \ + CURLINFO_CONNECT_TIME_T.3 \ CURLINFO_CONTENT_LENGTH_DOWNLOAD.3 \ CURLINFO_CONTENT_LENGTH_DOWNLOAD_T.3 \ CURLINFO_CONTENT_LENGTH_UPLOAD.3 \ CURLINFO_CONTENT_LENGTH_UPLOAD_T.3 \ CURLINFO_CONTENT_TYPE.3 \ CURLINFO_COOKIELIST.3 \ + CURLINFO_EARLYDATA_SENT_T.3 \ CURLINFO_EFFECTIVE_METHOD.3 \ CURLINFO_EFFECTIVE_URL.3 \ CURLINFO_FILETIME.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index cbabc48bf110d9..ddda26d8321300 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -435,6 +435,7 @@ CURLINFO_COOKIELIST 7.14.1 CURLINFO_DATA_IN 7.9.6 CURLINFO_DATA_OUT 7.9.6 CURLINFO_DOUBLE 7.4.1 +CURLINFO_EARLYDATA_SENT_T 8.11.0 CURLINFO_EFFECTIVE_METHOD 7.72.0 CURLINFO_EFFECTIVE_URL 7.4 CURLINFO_END 7.9.6 @@ -1054,6 +1055,7 @@ CURLSSLOPT_NATIVE_CA 7.71.0 CURLSSLOPT_NO_PARTIALCHAIN 7.68.0 CURLSSLOPT_NO_REVOKE 7.44.0 CURLSSLOPT_REVOKE_BEST_EFFORT 7.70.0 +CURLSSLOPT_EARLYDATA 8.11.0 CURLSSLSET_NO_BACKENDS 7.56.0 CURLSSLSET_OK 7.56.0 CURLSSLSET_TOO_LATE 7.56.0 diff --git a/docs/options-in-versions b/docs/options-in-versions index 62b61d94a8ff8b..a7a11630d3274d 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -246,6 +246,7 @@ --tftp-blksize 7.20.0 --tftp-no-options 7.48.0 --time-cond (-z) 5.8 +--tls-earlydata 8.11.0 --tls-max 7.54.0 --tls13-ciphers 7.61.0 --tlsauthtype 7.21.4 diff --git a/include/curl/curl.h b/include/curl/curl.h index 043b7103e8c4f9..18835586a1e335 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -30,7 +30,7 @@ */ #ifdef CURL_NO_OLDIES -#define CURL_STRICTER +#define CURL_STRICTER /* not used since 8.11.0 */ #endif /* Compile-time deprecation macros. */ @@ -114,13 +114,8 @@ extern "C" { #endif -#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) -typedef struct Curl_easy CURL; -typedef struct Curl_share CURLSH; -#else typedef void CURL; typedef void CURLSH; -#endif /* * libcurl external API function linkage decorations. @@ -254,12 +249,12 @@ typedef int (*curl_xferinfo_callback)(void *clientp, #endif #ifndef CURL_MAX_WRITE_SIZE - /* Tests have proven that 20K is a very bad buffer size for uploads on - Windows, while 16K for some odd reason performed a lot better. - We do the ifndef check to allow this value to easier be changed at build - time for those who feel adventurous. The practical minimum is about - 400 bytes since libcurl uses a buffer of this size as a scratch area - (unrelated to network send operations). */ + /* Tests have proven that 20K is a bad buffer size for uploads on Windows, + while 16K for some odd reason performed a lot better. We do the ifndef + check to allow this value to easier be changed at build time for those + who feel adventurous. The practical minimum is about 400 bytes since + libcurl uses a buffer of this size as a scratch area (unrelated to + network send operations). */ #define CURL_MAX_WRITE_SIZE 16384 #endif @@ -556,14 +551,14 @@ typedef enum { CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ CURLE_OBSOLETE32, /* 32 - NOT USED */ CURLE_RANGE_ERROR, /* 33 - RANGE "command" did not work */ - CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_OBSOLETE34, /* 34 */ CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ CURLE_BAD_DOWNLOAD_RESUME, /* 36 - could not resume download */ CURLE_FILE_COULDNT_READ_FILE, /* 37 */ CURLE_LDAP_CANNOT_BIND, /* 38 */ CURLE_LDAP_SEARCH_FAILED, /* 39 */ CURLE_OBSOLETE40, /* 40 - NOT USED */ - CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */ + CURLE_OBSOLETE41, /* 41 - NOT USED starting with 7.53.0 */ CURLE_ABORTED_BY_CALLBACK, /* 42 */ CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ CURLE_OBSOLETE44, /* 44 - NOT USED */ @@ -648,6 +643,12 @@ typedef enum { #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ +/* removed in 7.53.0 */ +#define CURLE_FUNCTION_NOT_FOUND CURLE_OBSOLETE41 + +/* removed in 7.56.0 */ +#define CURLE_HTTP_POST_ERROR CURLE_OBSOLETE34 + /* Previously obsolete error code reused in 7.38.0 */ #define CURLE_OBSOLETE16 CURLE_HTTP2 @@ -943,6 +944,9 @@ typedef enum { a client certificate for authentication. (Schannel) */ #define CURLSSLOPT_AUTO_CLIENT_CERT (1<<5) +/* If possible, send data using TLS 1.3 early data */ +#define CURLSSLOPT_EARLYDATA (1<<6) + /* The default connection attempt delay in milliseconds for happy eyeballs. CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document this value, keep them in sync. */ @@ -2954,7 +2958,8 @@ typedef enum { CURLINFO_QUEUE_TIME_T = CURLINFO_OFF_T + 65, CURLINFO_USED_PROXY = CURLINFO_LONG + 66, CURLINFO_POSTTRANSFER_TIME_T = CURLINFO_OFF_T + 67, - CURLINFO_LASTONE = 67 + CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68, + CURLINFO_LASTONE = 68 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as diff --git a/include/curl/curlver.h b/include/curl/curlver.h index e863815d41b404..6e97c13cc45ba8 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,13 +32,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.11.0-DEV" +#define LIBCURL_VERSION "8.11.1-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 #define LIBCURL_VERSION_MINOR 11 -#define LIBCURL_VERSION_PATCH 0 +#define LIBCURL_VERSION_PATCH 1 /* This is the numeric version of the libcurl version number, meant for easier parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will @@ -59,7 +59,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x080b00 +#define LIBCURL_VERSION_NUM 0x080b01 /* * This is the date and time when the full source package was created. The diff --git a/include/curl/multi.h b/include/curl/multi.h index 7b6c351ada718b..42469bb56570f1 100644 --- a/include/curl/multi.h +++ b/include/curl/multi.h @@ -54,11 +54,7 @@ extern "C" { #endif -#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) -typedef struct Curl_multi CURLM; -#else typedef void CURLM; -#endif typedef enum { CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or @@ -248,13 +244,13 @@ CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); * The data the returned pointer points to will not survive calling * curl_multi_cleanup(). * - * The 'CURLMsg' struct is meant to be very simple and only contain - * very basic information. If more involved information is wanted, - * we will provide the particular "transfer handle" in that struct - * and that should/could/would be used in subsequent - * curl_easy_getinfo() calls (or similar). The point being that we - * must never expose complex structs to applications, as then we will - * undoubtably get backwards compatibility problems in the future. + * The 'CURLMsg' struct is meant to be simple and only contain basic + * information. If more involved information is wanted, we will + * provide the particular "transfer handle" in that struct and that + * should/could/would be used in subsequent curl_easy_getinfo() calls + * (or similar). The point being that we must never expose complex + * structs to applications, as then we will undoubtably get backwards + * compatibility problems in the future. * * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out * of structs. It also writes the number of messages left in the diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bd3359f08a3010..895bc9165abca7 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -39,14 +39,14 @@ list(APPEND HHEADERS "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h") # The rest of the build include_directories( - "${CMAKE_CURRENT_BINARY_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}" + "${PROJECT_BINARY_DIR}/lib" # for "curl_config.h" + "${PROJECT_SOURCE_DIR}/lib" # for "curl_setup.h" ) if(USE_ARES) include_directories(SYSTEM ${CARES_INCLUDE_DIRS}) endif() -if(BUILD_TESTING) +if(CURL_BUILD_TESTING) add_library( curlu # special libcurlu library just for unittests STATIC @@ -112,7 +112,7 @@ if(SHARE_LIB_OBJECT) target_include_directories(${LIB_OBJECT} INTERFACE "$" - "$") + "$") set(LIB_SOURCE $) else() @@ -145,7 +145,7 @@ if(BUILD_STATIC_LIBS) target_include_directories(${LIB_STATIC} INTERFACE "$" - "$") + "$") endif() if(BUILD_SHARED_LIBS) @@ -163,7 +163,7 @@ if(BUILD_SHARED_LIBS) if(WIN32) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libcurl.rc") if(CURL_HIDES_PRIVATE_SYMBOLS) - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${CURL_SOURCE_DIR}/lib/libcurl.def") + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def") endif() endif() target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS}) @@ -184,7 +184,7 @@ if(BUILD_SHARED_LIBS) target_include_directories(${LIB_SHARED} INTERFACE "$" - "$") + "$") if(CMAKE_DLL_NAME_WITH_SOVERSION OR CYGWIN OR @@ -249,7 +249,9 @@ if(BUILD_SHARED_LIBS) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/libcurl.vers.in" "${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers" @ONLY) + include(CMakePushCheckState) include(CheckCSourceCompiles) + cmake_push_check_state() set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers") check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS) if(HAVE_VERSIONED_SYMBOLS) @@ -258,7 +260,7 @@ if(BUILD_SHARED_LIBS) else() message(WARNING "Versioned symbols requested, but not supported by the toolchain.") endif() - unset(CMAKE_REQUIRED_LINK_OPTIONS) + cmake_pop_check_state() endif() endif() diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 66680f3adba509..1d3f69a23c3ce5 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -97,9 +97,11 @@ LIB_VQUIC_HFILES = \ LIB_VSSH_CFILES = \ vssh/libssh.c \ vssh/libssh2.c \ + vssh/curl_path.c \ vssh/wolfssh.c -LIB_VSSH_HFILES = \ +LIB_VSSH_HFILES = \ + vssh/curl_path.h \ vssh/ssh.h LIB_CFILES = \ @@ -131,7 +133,6 @@ LIB_CFILES = \ curl_memrchr.c \ curl_multibyte.c \ curl_ntlm_core.c \ - curl_path.c \ curl_range.c \ curl_rtmp.c \ curl_sasl.c \ @@ -273,7 +274,6 @@ LIB_HFILES = \ curl_memrchr.h \ curl_multibyte.h \ curl_ntlm_core.h \ - curl_path.h \ curl_printf.h \ curl_range.h \ curl_rtmp.h \ diff --git a/lib/altsvc.c b/lib/altsvc.c index 6c040fbd4c839a..ea37b0afc1a6f6 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -94,6 +94,7 @@ static void altsvc_free(struct altsvc *as) static struct altsvc *altsvc_createid(const char *srchost, const char *dsthost, + size_t dlen, /* dsthost length */ enum alpnid srcalpnid, enum alpnid dstalpnid, unsigned int srcport, @@ -101,11 +102,9 @@ static struct altsvc *altsvc_createid(const char *srchost, { struct altsvc *as = calloc(1, sizeof(struct altsvc)); size_t hlen; - size_t dlen; if(!as) return NULL; hlen = strlen(srchost); - dlen = strlen(dsthost); DEBUGASSERT(hlen); DEBUGASSERT(dlen); if(!hlen || !dlen) { @@ -157,7 +156,8 @@ static struct altsvc *altsvc_create(char *srchost, enum alpnid srcalpnid = alpn2alpnid(srcalpn); if(!srcalpnid || !dstalpnid) return NULL; - return altsvc_createid(srchost, dsthost, srcalpnid, dstalpnid, + return altsvc_createid(srchost, dsthost, strlen(dsthost), + srcalpnid, dstalpnid, srcport, dstport); } @@ -490,8 +490,6 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, unsigned short srcport) { const char *p = value; - size_t len; - char namebuf[MAX_ALTSVC_HOSTLEN] = ""; char alpnbuf[MAX_ALTSVC_ALPNLEN] = ""; struct altsvc *as; unsigned short dstport = srcport; /* the same by default */ @@ -521,6 +519,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, p++; if(*p == '\"') { const char *dsthost = ""; + size_t dstlen = 0; /* destination hostname length */ const char *value_ptr; char option[32]; unsigned long num; @@ -535,32 +534,31 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, const char *hostp = p; if(*p == '[') { /* pass all valid IPv6 letters - does not handle zone id */ - len = strspn(++p, "0123456789abcdefABCDEF:."); - if(p[len] != ']') + dstlen = strspn(++p, "0123456789abcdefABCDEF:."); + if(p[dstlen] != ']') /* invalid host syntax, bail out */ break; /* we store the IPv6 numerical address *with* brackets */ - len += 2; - p = &p[len-1]; + dstlen += 2; + p = &p[dstlen-1]; } else { while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-'))) p++; - len = p - hostp; + dstlen = p - hostp; } - if(!len || (len >= MAX_ALTSVC_HOSTLEN)) { + if(!dstlen || (dstlen >= MAX_ALTSVC_HOSTLEN)) { infof(data, "Excessive alt-svc hostname, ignoring."); valid = FALSE; } else { - memcpy(namebuf, hostp, len); - namebuf[len] = 0; - dsthost = namebuf; + dsthost = hostp; } } else { /* no destination name, use source host */ dsthost = srchost; + dstlen = strlen(srchost); } if(*p == ':') { unsigned long port = 0; @@ -635,7 +633,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, this is the first entry of the line. */ altsvc_flush(asi, srcalpnid, srchost, srcport); - as = altsvc_createid(srchost, dsthost, + as = altsvc_createid(srchost, dsthost, dstlen, srcalpnid, dstalpnid, srcport, dstport); if(as) { diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index f6ad32ef36cd17..ae436f236e0519 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -290,23 +290,14 @@ static void destroy_async_data(struct Curl_async *async) int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks) { - struct timeval maxtime; + struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; struct timeval timebuf; - struct timeval *timeout; - long milli; int max = ares_getsock((ares_channel)data->state.async.resolver, (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE); - - maxtime.tv_sec = CURL_TIMEOUT_RESOLVE; - maxtime.tv_usec = 0; - - timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime, - &timebuf); - milli = (long)curlx_tvtoms(timeout); - if(milli == 0) - milli += 10; + struct timeval *timeout = + ares_timeout((ares_channel)data->state.async.resolver, &maxtime, &timebuf); + timediff_t milli = curlx_tvtoms(timeout); Curl_expire(data, milli, EXPIRE_ASYNC_NAME); - return max; } diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index a9fd21fa4d6b8c..6b7f9831bcb0a0 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -299,7 +299,7 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf, (checkprefix("Proxy-authenticate:", header) && (407 == k->httpcode))) { - bool proxy = (k->httpcode == 407) ? TRUE : FALSE; + bool proxy = (k->httpcode == 407); char *auth = Curl_copy_header_value(header); if(!auth) return CURLE_OUT_OF_MEMORY; diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index 038952d6417da8..e687dd10507cdc 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -277,6 +277,8 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf, { struct cf_h2_proxy_ctx *ctx = cf->ctx; nghttp2_option *o; + nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free, + Curl_nghttp2_calloc, Curl_nghttp2_realloc}; int rc = nghttp2_option_new(&o); if(rc) @@ -289,7 +291,7 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf, HTTP field value. */ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); #endif - rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); + rc = nghttp2_session_client_new3(&ctx->h2, cbs, cf, o, &mem); nghttp2_option_del(o); return rc; } diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index 95c105e338c260..dd7cdcb051401b 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -174,6 +174,7 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, { struct cf_hc_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; + int reply_ms; DEBUGASSERT(winner->cf); if(winner != &ctx->h3_baller) @@ -181,9 +182,15 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, if(winner != &ctx->h21_baller) cf_hc_baller_reset(&ctx->h21_baller, data); - CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", - winner->name, (int)Curl_timediff(Curl_now(), winner->started), - cf_hc_baller_reply_ms(winner, data)); + reply_ms = cf_hc_baller_reply_ms(winner, data); + if(reply_ms >= 0) + CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", + winner->name, (int)Curl_timediff(Curl_now(), winner->started), + reply_ms); + else + CURL_TRC_CF(data, cf, "deferred handshake %s: %dms", + winner->name, (int)Curl_timediff(Curl_now(), winner->started)); + cf->next = winner->cf; winner->cf = NULL; diff --git a/lib/cf-socket.c b/lib/cf-socket.c index e370d48acf7e4d..497a3b965f1250 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -600,36 +600,39 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, if(!iface && !host && !port) /* no local kind of binding was requested */ return CURLE_OK; + else if(iface && (strlen(iface) >= 255) ) + return CURLE_BAD_FUNCTION_ARGUMENT; memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); - if(iface && (strlen(iface) < 255) ) { + if(iface || host) { char myhost[256] = ""; int done = 0; /* -1 for error, 1 for address found */ if2ip_result_t if2ip_result = IF2IP_NOT_FOUND; - /* interface */ #ifdef SO_BINDTODEVICE - /* - * This binds the local socket to a particular interface. This will - * force even requests to other local interfaces to go out the external - * interface. Only bind to the interface when specified as interface, - * not just as a hostname or ip address. - * - * The interface might be a VRF, eg: vrf-blue, which means it cannot be - * converted to an IP address and would fail Curl_if2ip. Simply try to - * use it straight away. - */ - if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, - iface, (curl_socklen_t)strlen(iface) + 1) == 0) { - /* This is often "errno 1, error: Operation not permitted" if you are - * not running as root or another suitable privileged user. If it - * succeeds it means the parameter was a valid interface and not an IP - * address. Return immediately. - */ - if(!host_input) { - infof(data, "socket successfully bound to interface '%s'", iface); - return CURLE_OK; + if(iface) { + /* + * This binds the local socket to a particular interface. This will + * force even requests to other local interfaces to go out the external + * interface. Only bind to the interface when specified as interface, + * not just as a hostname or ip address. + * + * The interface might be a VRF, eg: vrf-blue, which means it cannot be + * converted to an IP address and would fail Curl_if2ip. Simply try to + * use it straight away. + */ + if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, + iface, (curl_socklen_t)strlen(iface) + 1) == 0) { + /* This is often "errno 1, error: Operation not permitted" if you are + * not running as root or another suitable privileged user. If it + * succeeds it means the parameter was a valid interface and not an IP + * address. Return immediately. + */ + if(!host_input) { + infof(data, "socket successfully bound to interface '%s'", iface); + return CURLE_OK; + } } } #endif @@ -1310,7 +1313,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf, if(blocking) return CURLE_UNSUPPORTED_PROTOCOL; - *done = FALSE; /* a very negative world view is best */ + *done = FALSE; /* a negative world view is best */ if(ctx->sock == CURL_SOCKET_BAD) { int error; @@ -1643,7 +1646,7 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data) cf->conn->primary = ctx->ip; cf->conn->remote_addr = &ctx->addr; #ifdef USE_IPV6 - cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6) ? TRUE : FALSE; + cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6); #endif } else { @@ -1751,7 +1754,7 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, } case CF_QUERY_IP_INFO: #ifdef USE_IPV6 - *pres1 = (ctx->addr.family == AF_INET6) ? TRUE : FALSE; + *pres1 = (ctx->addr.family == AF_INET6); #else *pres1 = FALSE; #endif @@ -2017,10 +2020,84 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, return result; } +static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; + timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT; + timediff_t other; + struct curltime now; + +#ifndef CURL_DISABLE_FTP + if(data->set.accepttimeout > 0) + timeout_ms = data->set.accepttimeout; +#endif + + now = Curl_now(); + /* check if the generic timeout possibly is set shorter */ + other = Curl_timeleft(data, &now, FALSE); + if(other && (other < timeout_ms)) + /* note that this also works fine for when other happens to be negative + due to it already having elapsed */ + timeout_ms = other; + else { + /* subtract elapsed time */ + timeout_ms -= Curl_timediff(now, ctx->started_at); + if(!timeout_ms) + /* avoid returning 0 as that means no timeout! */ + timeout_ms = -1; + } + return timeout_ms; +} + +static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_socket_ctx *ctx = cf->ctx; +#ifdef HAVE_GETPEERNAME + char buffer[STRERROR_LEN]; + struct Curl_sockaddr_storage ssrem; + curl_socklen_t plen; + + ctx->ip.remote_ip[0] = 0; + ctx->ip.remote_port = 0; + plen = sizeof(ssrem); + memset(&ssrem, 0, plen); + if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { + int error = SOCKERRNO; + failf(data, "getpeername() failed with errno %d: %s", + error, Curl_strerror(error, buffer, sizeof(buffer))); + return; + } + if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, + ctx->ip.remote_ip, &ctx->ip.remote_port)) { + failf(data, "ssrem inet_ntop() failed with errno %d: %s", + errno, Curl_strerror(errno, buffer, sizeof(buffer))); + return; + } +#else + ctx->ip.remote_ip[0] = 0; + ctx->ip.remote_port = 0; + (void)data; +#endif +} + static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done) { + struct cf_socket_ctx *ctx = cf->ctx; +#ifdef USE_IPV6 + struct Curl_sockaddr_storage add; +#else + struct sockaddr_in add; +#endif + curl_socklen_t size = (curl_socklen_t) sizeof(add); + curl_socket_t s_accepted = CURL_SOCKET_BAD; + timediff_t timeout_ms; + int socketstate = 0; + bool incoming = FALSE; + /* we start accepted, if we ever close, we cannot go on */ (void)data; (void)blocking; @@ -2028,7 +2105,79 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, *done = TRUE; return CURLE_OK; } - return CURLE_FAILED_INIT; + + timeout_ms = cf_tcp_accept_timeleft(cf, data); + if(timeout_ms < 0) { + /* if a timeout was already reached, bail out */ + failf(data, "Accept timeout occurred while waiting server connect"); + return CURLE_FTP_ACCEPT_TIMEOUT; + } + + CURL_TRC_CF(data, cf, "Checking for incoming on fd=%" FMT_SOCKET_T + " ip=%s:%d", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port); + socketstate = Curl_socket_check(ctx->sock, CURL_SOCKET_BAD, + CURL_SOCKET_BAD, 0); + CURL_TRC_CF(data, cf, "socket_check -> %x", socketstate); + switch(socketstate) { + case -1: /* error */ + /* let's die here */ + failf(data, "Error while waiting for server connect"); + return CURLE_FTP_ACCEPT_FAILED; + default: + if(socketstate & CURL_CSELECT_IN) { + infof(data, "Ready to accept data connection from server"); + incoming = TRUE; + } + break; + } + + if(!incoming) { + CURL_TRC_CF(data, cf, "nothing heard from the server yet"); + *done = FALSE; + return CURLE_OK; + } + + if(0 == getsockname(ctx->sock, (struct sockaddr *) &add, &size)) { + size = sizeof(add); + s_accepted = accept(ctx->sock, (struct sockaddr *) &add, &size); + } + + if(CURL_SOCKET_BAD == s_accepted) { + failf(data, "Error accept()ing server connect"); + return CURLE_FTP_PORT_FAILED; + } + + infof(data, "Connection accepted from server"); + (void)curlx_nonblock(s_accepted, TRUE); /* enable non-blocking */ + /* Replace any filter on SECONDARY with one listening on this socket */ + ctx->listening = FALSE; + ctx->accepted = TRUE; + socket_close(data, cf->conn, TRUE, ctx->sock); + ctx->sock = s_accepted; + + cf->conn->sock[cf->sockindex] = ctx->sock; + cf_tcp_set_accepted_remote_ip(cf, data); + set_local_ip(cf, data); + ctx->active = TRUE; + ctx->connected_at = Curl_now(); + cf->connected = TRUE; + CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T + ", remote=%s port=%d)", + ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port); + + if(data->set.fsockopt) { + int error = 0; + + /* activate callback for setting socket options */ + Curl_set_in_callback(data, true); + error = data->set.fsockopt(data->set.sockopt_client, + ctx->sock, CURLSOCKTYPE_ACCEPT); + Curl_set_in_callback(data, false); + + if(error) + return CURLE_ABORTED_BY_CALLBACK; + } + return CURLE_OK; } struct Curl_cftype Curl_cft_tcp_accept = { @@ -2076,13 +2225,12 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, goto out; Curl_conn_cf_add(data, conn, sockindex, cf); + ctx->started_at = Curl_now(); conn->sock[sockindex] = ctx->sock; set_local_ip(cf, data); - ctx->active = TRUE; - ctx->connected_at = Curl_now(); - cf->connected = TRUE; - CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%" FMT_SOCKET_T ")", - ctx->sock); + CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T + " ip=%s:%d", ctx->sock, + ctx->ip.local_ip, ctx->ip.local_port); out: if(result) { @@ -2092,67 +2240,16 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, return result; } -static void set_accepted_remote_ip(struct Curl_cfilter *cf, - struct Curl_easy *data) +bool Curl_conn_is_tcp_listen(struct Curl_easy *data, + int sockindex) { - struct cf_socket_ctx *ctx = cf->ctx; -#ifdef HAVE_GETPEERNAME - char buffer[STRERROR_LEN]; - struct Curl_sockaddr_storage ssrem; - curl_socklen_t plen; - - ctx->ip.remote_ip[0] = 0; - ctx->ip.remote_port = 0; - plen = sizeof(ssrem); - memset(&ssrem, 0, plen); - if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { - int error = SOCKERRNO; - failf(data, "getpeername() failed with errno %d: %s", - error, Curl_strerror(error, buffer, sizeof(buffer))); - return; + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + while(cf) { + if(cf->cft == &Curl_cft_tcp_accept) + return TRUE; + cf = cf->next; } - if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, - ctx->ip.remote_ip, &ctx->ip.remote_port)) { - failf(data, "ssrem inet_ntop() failed with errno %d: %s", - errno, Curl_strerror(errno, buffer, sizeof(buffer))); - return; - } -#else - ctx->ip.remote_ip[0] = 0; - ctx->ip.remote_port = 0; - (void)data; -#endif -} - -CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, curl_socket_t *s) -{ - struct Curl_cfilter *cf = NULL; - struct cf_socket_ctx *ctx = NULL; - - cf = conn->cfilter[sockindex]; - if(!cf || cf->cft != &Curl_cft_tcp_accept) - return CURLE_FAILED_INIT; - - ctx = cf->ctx; - DEBUGASSERT(ctx->listening); - /* discard the listen socket */ - socket_close(data, conn, TRUE, ctx->sock); - ctx->listening = FALSE; - ctx->sock = *s; - conn->sock[sockindex] = ctx->sock; - set_accepted_remote_ip(cf, data); - set_local_ip(cf, data); - ctx->active = TRUE; - ctx->accepted = TRUE; - ctx->connected_at = Curl_now(); - cf->connected = TRUE; - CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T - ", remote=%s port=%d)", - ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port); - - return CURLE_OK; + return FALSE; } /** diff --git a/lib/cf-socket.h b/lib/cf-socket.h index 6374e7c92b4ed9..651034901cd923 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -147,12 +147,11 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, curl_socket_t *s); /** - * Replace the listen socket with the accept()ed one. + * Return TRUE iff the last filter at `sockindex` was set via + * Curl_conn_tcp_listen_set(). */ -CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - curl_socket_t *s); +bool Curl_conn_is_tcp_listen(struct Curl_easy *data, + int sockindex); /** * Peek at the socket and remote ip/port the socket filter is using. diff --git a/lib/config-amigaos.h b/lib/config-amigaos.h index d168b446b6784a..11e5999493ddc8 100644 --- a/lib/config-amigaos.h +++ b/lib/config-amigaos.h @@ -71,8 +71,8 @@ #define USE_MANUAL 1 #define CURL_DISABLE_LDAP 1 -#ifndef OS -#define OS "AmigaOS" +#ifndef CURL_OS +#define CURL_OS "AmigaOS" #endif #define PACKAGE "curl" diff --git a/lib/config-dos.h b/lib/config-dos.h index c6fbba796a1a2e..4e9724726e5c23 100644 --- a/lib/config-dos.h +++ b/lib/config-dos.h @@ -29,13 +29,13 @@ /* lib/config-dos.h - Hand crafted config file for DOS */ /* ================================================================ */ -#ifndef OS +#ifndef CURL_OS #if defined(DJGPP) - #define OS "MSDOS/djgpp" + #define CURL_OS "MSDOS/djgpp" #elif defined(__HIGHC__) - #define OS "MSDOS/HighC" + #define CURL_OS "MSDOS/HighC" #else - #define OS "MSDOS/?" + #define CURL_OS "MSDOS/?" #endif #endif diff --git a/lib/config-mac.h b/lib/config-mac.h index 53df87cbbc50d1..8d6210c77e8264 100644 --- a/lib/config-mac.h +++ b/lib/config-mac.h @@ -30,8 +30,8 @@ /* On macOS you must run configure to generate curl_config.h file */ /* =================================================================== */ -#ifndef OS -#define OS "mac" +#ifndef CURL_OS +#define CURL_OS "mac" #endif #include diff --git a/lib/config-os400.h b/lib/config-os400.h index 29aa818fbc29e7..88259d95f4be05 100644 --- a/lib/config-os400.h +++ b/lib/config-os400.h @@ -36,8 +36,8 @@ #undef VERSION /* Define cpu-machine-OS */ -#ifndef OS -#define OS "OS/400" +#ifndef CURL_OS +#define CURL_OS "OS/400" #endif /* OS400 supports a 3-argument ASCII version of gethostbyaddr_r(), but its diff --git a/lib/config-plan9.h b/lib/config-plan9.h index 1270e108dd2574..699291a1eddb93 100644 --- a/lib/config-plan9.h +++ b/lib/config-plan9.h @@ -32,8 +32,8 @@ #define CURL_DISABLE_LDAP 1 #define NEED_REENTRANT 1 -#ifndef OS -#define OS "plan9" +#ifndef CURL_OS +#define CURL_OS "plan9" #endif #define PACKAGE "curl" #define PACKAGE_NAME "curl" diff --git a/lib/config-riscos.h b/lib/config-riscos.h index 580e822e510db7..35b28cc5345b58 100644 --- a/lib/config-riscos.h +++ b/lib/config-riscos.h @@ -35,8 +35,8 @@ #undef VERSION /* Define cpu-machine-OS */ -#ifndef OS -#define OS "ARM-RISC OS" +#ifndef CURL_OS +#define CURL_OS "ARM-RISC OS" #endif /* Define if you want the built-in manual */ diff --git a/lib/config-win32.h b/lib/config-win32.h index b855e5c635f75f..2daed912d0e9bd 100644 --- a/lib/config-win32.h +++ b/lib/config-win32.h @@ -155,6 +155,9 @@ /* Define if you have the setmode function. */ #define HAVE_SETMODE 1 +/* Define if you have the _setmode function. */ +#define HAVE__SETMODE 1 + /* Define if you have the socket function. */ #define HAVE_SOCKET 1 @@ -424,10 +427,6 @@ Vista # endif #endif -#ifdef USE_WIN32_LARGE_FILES -#define HAVE__FSEEKI64 -#endif - /* Define to the size of `off_t', as computed by sizeof. */ #if defined(__MINGW32__) && \ defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) @@ -487,19 +486,19 @@ Vista /* ---------------------------------------------------------------- */ /* Define cpu-machine-OS */ -#ifndef OS +#ifndef CURL_OS #if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */ -#define OS "i386-pc-win32" +#define CURL_OS "i386-pc-win32" #elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (MSVC >=2005 or gcc) */ -#define OS "x86_64-pc-win32" +#define CURL_OS "x86_64-pc-win32" #elif defined(_M_IA64) || defined(__ia64__) /* Itanium */ -#define OS "ia64-pc-win32" +#define CURL_OS "ia64-pc-win32" #elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 (Windows RT) */ -#define OS "thumbv7a-pc-win32" +#define CURL_OS "thumbv7a-pc-win32" #elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */ -#define OS "aarch64-pc-win32" +#define CURL_OS "aarch64-pc-win32" #else -#define OS "unknown-pc-win32" +#define CURL_OS "unknown-pc-win32" #endif #endif diff --git a/lib/config-win32ce.h b/lib/config-win32ce.h index 800fc61cf1ecd1..80765f1f655f1c 100644 --- a/lib/config-win32ce.h +++ b/lib/config-win32ce.h @@ -271,8 +271,8 @@ /* ---------------------------------------------------------------- */ /* Define cpu-machine-OS */ -#ifndef OS -#define OS "i386-pc-win32ce" +#ifndef CURL_OS +#define CURL_OS "i386-pc-win32ce" #endif /* Name of package */ diff --git a/lib/conncache.c b/lib/conncache.c index 631c9f18bf5da2..589205bc15297f 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -72,7 +72,7 @@ } while(0) -/* A list of connections to the same destinationn. */ +/* A list of connections to the same destination. */ struct cpool_bundle { struct Curl_llist conns; /* connections in the bundle */ size_t dest_len; /* total length of destination, including NUL */ @@ -170,7 +170,7 @@ int Curl_cpool_init(struct cpool *cpool, * Probably better to have an internal handle owned by the multi that * can be used for cpool operations. */ cpool->idata->multi = multi; - #ifdef DEBUGBUILD +#ifdef DEBUGBUILD if(getenv("CURL_DEBUG")) cpool->idata->set.verbose = TRUE; #endif @@ -269,25 +269,10 @@ cpool_add_bundle(struct cpool *cpool, struct connectdata *conn) static void cpool_remove_bundle(struct cpool *cpool, struct cpool_bundle *bundle) { - struct Curl_hash_iterator iter; - struct Curl_hash_element *he; - if(!cpool) return; - Curl_hash_start_iterate(&cpool->dest2bundle, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - if(he->ptr == bundle) { - /* The bundle is destroyed by the hash destructor function, - free_bundle_hash_entry() */ - Curl_hash_delete(&cpool->dest2bundle, he->key, he->key_len); - return; - } - - he = Curl_hash_next_element(&iter); - } + Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len); } static struct connectdata * @@ -329,6 +314,9 @@ int Curl_cpool_check_limits(struct Curl_easy *data, "limit of %zu", oldest_idle->connection_id, Curl_llist_count(&bundle->conns), dest_limit)); Curl_cpool_disconnect(data, oldest_idle, FALSE); + + /* in case the bundle was destroyed in disconnect, look it up again */ + bundle = cpool_find_bundle(cpool, conn); } if(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) { result = CPOOL_LIMIT_DEST; @@ -409,7 +397,7 @@ static void cpool_remove_conn(struct cpool *cpool, cpool->num_conn--; } else { - /* Not in a bundle, already in the shutdown list? */ + /* Not in a bundle, already in the shutdown list? */ DEBUGASSERT(list == &cpool->shutdowns); } } diff --git a/lib/connect.c b/lib/connect.c index debaf355f2306c..ee3b4c8a692942 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -619,7 +619,7 @@ static CURLcode is_connected(struct Curl_cfilter *cf, * If transport is QUIC, we need to shutdown the ongoing 'other' * cot ballers in a QUIC appropriate way. */ evaluate: - *connected = FALSE; /* a very negative world view is best */ + *connected = FALSE; /* a negative world view is best */ now = Curl_now(); ongoing = not_started = 0; for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { diff --git a/lib/content_encoding.c b/lib/content_encoding.c index 67c2fb758cea74..a4b16dda10310e 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -33,13 +33,13 @@ #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) /* Ignore -Wvla warnings in brotli headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvla" #endif #include -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif #endif diff --git a/lib/cookie.c b/lib/cookie.c index 36d1147b5d98cc..773e5357d3e271 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -28,33 +28,20 @@ RECEIVING COOKIE INFORMATION ============================ -struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, - const char *file, struct CookieInfo *inc, bool newsession); +Curl_cookie_init() Inits a cookie struct to store data in a local file. This is always called before any cookies are set. -struct Cookie *Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *c, bool httpheader, bool noexpire, - char *lineptr, const char *domain, const char *path, - bool secure); - - The 'lineptr' parameter is a full "Set-cookie:" line as - received from a server. - - The function need to replace previously stored lines that this new - line supersedes. +Curl_cookie_add() - It may remove lines that are expired. - - It should return an indication of success/error. + Adds a cookie to the in-memory cookie jar. SENDING COOKIE INFORMATION ========================== -struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, - char *host, char *path, bool secure); +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 @@ -63,7 +50,6 @@ struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, It shall only return cookies that have not expired. - Example set of cookies: Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure @@ -102,6 +88,7 @@ Example set of cookies: #include "rename.h" #include "fopen.h" #include "strdup.h" +#include "llist.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -335,17 +322,17 @@ void Curl_cookie_loadfiles(struct Curl_easy *data) if(list) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); while(list) { - struct CookieInfo *newcookies = + struct CookieInfo *ci = Curl_cookie_init(data, list->data, data->cookies, data->set.cookiesession); - if(!newcookies) + if(!ci) /* * Failure may be due to OOM or a bad cookie; both are ignored * but only the first should be */ infof(data, "ignoring failed cookie_init for %s", list->data); else - data->cookies = newcookies; + data->cookies = ci; list = list->next; } Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); @@ -378,9 +365,9 @@ static void strstore(char **str, const char *newstr, size_t len) * more cookies expire, then processing will exit early in case this timestamp * is in the future. */ -static void remove_expired(struct CookieInfo *cookies) +static void remove_expired(struct CookieInfo *ci) { - struct Cookie *co, *nx; + struct Cookie *co; curl_off_t now = (curl_off_t)time(NULL); unsigned int i; @@ -392,37 +379,32 @@ static void remove_expired(struct CookieInfo *cookies) * recorded first expiration is the max offset, then perform the safe * fallback of checking all cookies. */ - if(now < cookies->next_expiration && - cookies->next_expiration != CURL_OFF_T_MAX) + if(now < ci->next_expiration && + ci->next_expiration != CURL_OFF_T_MAX) return; else - cookies->next_expiration = CURL_OFF_T_MAX; + ci->next_expiration = CURL_OFF_T_MAX; for(i = 0; i < COOKIE_HASH_SIZE; i++) { - struct Cookie *pv = NULL; - co = cookies->cookies[i]; - while(co) { - nx = co->next; + struct Curl_llist_node *n; + struct Curl_llist_node *e = NULL; + + for(n = Curl_llist_head(&ci->cookielist[i]); n; n = e) { + co = Curl_node_elem(n); + e = Curl_node_next(n); if(co->expires && co->expires < now) { - if(!pv) { - cookies->cookies[i] = co->next; - } - else { - pv->next = co->next; - } - cookies->numcookies--; + Curl_node_remove(n); freecookie(co); + ci->numcookies--; } else { /* - * If this cookie has an expiration timestamp earlier than what we have - * seen so far then record it for the next round of expirations. + * If this cookie has an expiration timestamp earlier than what we + * have seen so far then record it for the next round of expirations. */ - if(co->expires && co->expires < cookies->next_expiration) - cookies->next_expiration = co->expires; - pv = co; + if(co->expires && co->expires < ci->next_expiration) + ci->next_expiration = co->expires; } - co = nx; } } } @@ -470,558 +452,489 @@ static int invalid_octets(const char *p) return (p[len] != '\0'); } -/* - * Curl_cookie_add - * - * Add a single cookie line to the cookie keeping object. Be aware that - * sometimes we get an IP-only hostname, and that might also be a numerical - * IPv6 address. - * - * Returns NULL on out of memory or invalid cookie. This is suboptimal, - * as they should be treated separately. - */ -struct Cookie * -Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *c, - bool httpheader, /* TRUE if HTTP header-style line */ - bool noexpire, /* if TRUE, skip remove_expired() */ - const char *lineptr, /* first character of the line */ - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is set, - used to get default path for the cookie - unless set */ - bool secure) /* TRUE if connection is over secure origin */ +#define CERR_OK 0 +#define CERR_TOO_LONG 1 /* input line too long */ +#define CERR_TAB 2 /* in a wrong place */ +#define CERR_TOO_BIG 3 /* name/value too large */ +#define CERR_BAD 4 /* deemed incorrect */ +#define CERR_NO_SEP 5 /* semicolon problem */ +#define CERR_NO_NAME_VALUE 6 /* name or value problem */ +#define CERR_INVALID_OCTET 7 /* bad content */ +#define CERR_BAD_SECURE 8 /* secure in a bad place */ +#define CERR_OUT_OF_MEMORY 9 +#define CERR_NO_TAILMATCH 10 +#define CERR_COMMENT 11 /* a commented line */ +#define CERR_RANGE 12 /* expire range problem */ +#define CERR_FIELDS 13 /* incomplete netscape line */ +#define CERR_PSL 14 /* a public suffix */ +#define CERR_LIVE_WINS 15 + +static int +parse_cookie_header(struct Curl_easy *data, + struct Cookie *co, + struct CookieInfo *ci, + const char *ptr, + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is + set, used to get default path for + the cookie unless set */ + bool secure) /* TRUE if connection is over secure + origin */ { - struct Cookie *clist; - struct Cookie *co; - struct Cookie *lastc = NULL; - struct Cookie *replace_co = NULL; - struct Cookie *replace_clist = NULL; - time_t now = time(NULL); - bool replace_old = FALSE; - bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ - size_t myhash; - - DEBUGASSERT(data); - DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ - if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) - return NULL; - - /* First, alloc and init a new struct for it */ - co = calloc(1, sizeof(struct Cookie)); - if(!co) - return NULL; /* bail out if we are this low on memory */ - - if(httpheader) { - /* This line was read off an HTTP-header */ - const char *ptr; + /* This line was read off an HTTP-header */ + time_t now; + size_t linelength = strlen(ptr); + if(linelength > MAX_COOKIE_LINE) + /* discard overly long lines at once */ + return CERR_TOO_LONG; + + now = time(NULL); + do { + size_t vlen; + size_t nlen; + + while(*ptr && ISBLANK(*ptr)) + ptr++; + + /* we have a = pair or a stand-alone word here */ + nlen = strcspn(ptr, ";\t\r\n="); + if(nlen) { + bool done = FALSE; + bool sep = FALSE; + const char *namep = ptr; + const char *valuep; + + ptr += nlen; + + /* trim trailing spaces and tabs after name */ + while(nlen && ISBLANK(namep[nlen - 1])) + nlen--; + + if(*ptr == '=') { + vlen = strcspn(++ptr, ";\r\n"); + valuep = ptr; + sep = TRUE; + ptr = &valuep[vlen]; + + /* Strip off trailing whitespace from the value */ + while(vlen && ISBLANK(valuep[vlen-1])) + vlen--; + + /* Skip leading whitespace from the value */ + while(vlen && ISBLANK(*valuep)) { + valuep++; + vlen--; + } - size_t linelength = strlen(lineptr); - if(linelength > MAX_COOKIE_LINE) { - /* discard overly long lines at once */ - free(co); - return NULL; - } + /* Reject cookies with a TAB inside the value */ + if(memchr(valuep, '\t', vlen)) { + infof(data, "cookie contains TAB, dropping"); + return CERR_TAB; + } + } + else { + valuep = NULL; + vlen = 0; + } - ptr = lineptr; - do { - size_t vlen; - size_t nlen; + /* + * Check for too long individual name or contents, or too long + * combination of name + contents. Chrome and Firefox support 4095 or + * 4096 bytes combo + */ + if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) || + ((nlen + vlen) > MAX_NAME)) { + infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", + nlen, vlen); + return CERR_TOO_BIG; + } - while(*ptr && ISBLANK(*ptr)) - ptr++; + /* + * Check if we have a reserved prefix set before anything else, as we + * otherwise have to test for the prefix in both the cookie name and + * "the rest". Prefixes must start with '__' and end with a '-', so + * only test for names where that can possibly be true. + */ + if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') { + if(strncasecompare("__Secure-", namep, 9)) + co->prefix_secure = TRUE; + else if(strncasecompare("__Host-", namep, 7)) + co->prefix_host = TRUE; + } - /* we have a = pair or a stand-alone word here */ - nlen = strcspn(ptr, ";\t\r\n="); - if(nlen) { - bool done = FALSE; - bool sep = FALSE; - const char *namep = ptr; - const char *valuep; - - ptr += nlen; - - /* trim trailing spaces and tabs after name */ - while(nlen && ISBLANK(namep[nlen - 1])) - nlen--; - - if(*ptr == '=') { - vlen = strcspn(++ptr, ";\r\n"); - valuep = ptr; - sep = TRUE; - ptr = &valuep[vlen]; - - /* Strip off trailing whitespace from the value */ - while(vlen && ISBLANK(valuep[vlen-1])) - vlen--; - - /* Skip leading whitespace from the value */ - while(vlen && ISBLANK(*valuep)) { - valuep++; - vlen--; - } + /* + * Use strstore() below to properly deal with received cookie + * headers that have the same string property set more than once, + * and then we use the last one. + */ - /* Reject cookies with a TAB inside the value */ - if(memchr(valuep, '\t', vlen)) { - freecookie(co); - infof(data, "cookie contains TAB, dropping"); - return NULL; - } + if(!co->name) { + /* The very first name/value pair is the actual cookie name */ + if(!sep) + /* Bad name/value pair. */ + return CERR_NO_SEP; + + strstore(&co->name, namep, nlen); + strstore(&co->value, valuep, vlen); + done = TRUE; + if(!co->name || !co->value) + return CERR_NO_NAME_VALUE; + + if(invalid_octets(co->value) || invalid_octets(co->name)) { + infof(data, "invalid octets in name/value, cookie dropped"); + return CERR_INVALID_OCTET; } - else { - valuep = NULL; - vlen = 0; - } - + } + else if(!vlen) { + /* + * this was a "=" with no content, and we must allow + * 'secure' and 'httponly' specified this weirdly + */ + done = TRUE; /* - * Check for too long individual name or contents, or too long - * combination of name + contents. Chrome and Firefox support 4095 or - * 4096 bytes combo + * 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(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) || - ((nlen + vlen) > MAX_NAME)) { - freecookie(co); - infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", - nlen, vlen); - return NULL; + if((nlen == 6) && strncasecompare("secure", namep, 6)) { + if(secure || !ci->running) { + co->secure = TRUE; + } + else { + return CERR_BAD_SECURE; + } } + else if((nlen == 8) && strncasecompare("httponly", namep, 8)) + co->httponly = TRUE; + else if(sep) + /* there was a '=' so we are not done parsing this field */ + done = FALSE; + } + if(done) + ; + else if((nlen == 4) && strncasecompare("path", namep, 4)) { + strstore(&co->path, valuep, vlen); + if(!co->path) + return CERR_OUT_OF_MEMORY; + free(co->spath); /* if this is set again */ + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) + return CERR_OUT_OF_MEMORY; + } + else if((nlen == 6) && + strncasecompare("domain", namep, 6) && vlen) { + bool is_ip; /* - * Check if we have a reserved prefix set before anything else, as we - * otherwise have to test for the prefix in both the cookie name and - * "the rest". Prefixes must start with '__' and end with a '-', so - * only test for names where that can possibly be true. + * 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(nlen >= 7 && namep[0] == '_' && namep[1] == '_') { - if(strncasecompare("__Secure-", namep, 9)) - co->prefix |= COOKIE_PREFIX__SECURE; - else if(strncasecompare("__Host-", namep, 7)) - co->prefix |= COOKIE_PREFIX__HOST; + + if('.' == valuep[0]) { + valuep++; /* ignore preceding dot */ + vlen--; } +#ifndef USE_LIBPSL /* - * Use strstore() below to properly deal with received cookie - * headers that have the same string property set more than once, - * and then we use the last one. + * 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(valuep, vlen)) + domain = ":"; +#endif - if(!co->name) { - /* The very first name/value pair is the actual cookie name */ - if(!sep) { - /* Bad name/value pair. */ - badcookie = TRUE; - break; - } - strstore(&co->name, namep, nlen); - strstore(&co->value, valuep, vlen); - done = TRUE; - if(!co->name || !co->value) { - badcookie = TRUE; - break; - } - if(invalid_octets(co->value) || invalid_octets(co->name)) { - infof(data, "invalid octets in name/value, cookie dropped"); - badcookie = TRUE; - break; - } + is_ip = Curl_host_is_ipnum(domain ? domain : valuep); + + if(!domain + || (is_ip && !strncmp(valuep, domain, vlen) && + (vlen == strlen(domain))) + || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) { + strstore(&co->domain, valuep, vlen); + if(!co->domain) + return CERR_OUT_OF_MEMORY; + + if(!is_ip) + co->tailmatch = TRUE; /* we always do that if the domain name was + given */ } - else if(!vlen) { - /* - * this was a "=" with no content, and we must allow - * 'secure' and 'httponly' specified this weirdly - */ - done = TRUE; + else { /* - * 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 + * 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. */ - if((nlen == 6) && strncasecompare("secure", namep, 6)) { - if(secure || !c->running) { - co->secure = TRUE; - } - else { - badcookie = TRUE; - break; - } - } - else if((nlen == 8) && strncasecompare("httponly", namep, 8)) - co->httponly = TRUE; - else if(sep) - /* there was a '=' so we are not done parsing this field */ - done = FALSE; + infof(data, "skipped cookie with bad tailmatch domain: %s", + valuep); + return CERR_NO_TAILMATCH; } - if(done) - ; - else if((nlen == 4) && strncasecompare("path", namep, 4)) { - strstore(&co->path, valuep, vlen); - if(!co->path) { - badcookie = TRUE; /* out of memory bad */ - break; - } - free(co->spath); /* if this is set again */ - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) { - badcookie = TRUE; /* out of memory bad */ - break; - } + } + else if((nlen == 7) && strncasecompare("version", namep, 7)) { + /* just ignore */ + } + else if((nlen == 7) && strncasecompare("max-age", namep, 7)) { + /* + * 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. + */ + CURLofft offt; + const char *maxage = valuep; + offt = curlx_strtoofft((*maxage == '\"') ? + &maxage[1] : &maxage[0], NULL, 10, + &co->expires); + switch(offt) { + case CURL_OFFT_FLOW: + /* overflow, used max value */ + co->expires = CURL_OFF_T_MAX; + break; + case CURL_OFFT_INVAL: + /* negative or otherwise bad, expire */ + co->expires = 1; + break; + case CURL_OFFT_OK: + if(!co->expires) + /* already expired */ + co->expires = 1; + else if(CURL_OFF_T_MAX - now < co->expires) + /* would overflow */ + co->expires = CURL_OFF_T_MAX; + else + co->expires += now; + break; } - else if((nlen == 6) && - strncasecompare("domain", namep, 6) && vlen) { - bool is_ip; - - /* - * 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('.' == valuep[0]) { - valuep++; /* ignore preceding dot */ - vlen--; - } - -#ifndef USE_LIBPSL + } + else if((nlen == 7) && strncasecompare("expires", namep, 7)) { + if(!co->expires) { /* - * 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". + * Let max-age have priority. + * + * If the date cannot get parsed for whatever reason, the cookie + * will be treated as a session cookie */ - if(bad_domain(valuep, vlen)) - domain = ":"; -#endif + co->expires = Curl_getdate_capped(valuep); - is_ip = Curl_host_is_ipnum(domain ? domain : valuep); - - if(!domain - || (is_ip && !strncmp(valuep, domain, vlen) && - (vlen == strlen(domain))) - || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) { - strstore(&co->domain, valuep, vlen); - if(!co->domain) { - badcookie = TRUE; - break; - } - 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. - */ - badcookie = TRUE; - infof(data, "skipped cookie with bad tailmatch domain: %s", - valuep); - } - } - else if((nlen == 7) && strncasecompare("version", namep, 7)) { - /* just ignore */ - } - else if((nlen == 7) && strncasecompare("max-age", namep, 7)) { /* - * 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. + * Session cookies have expires set to 0 so if we get that back + * from the date parser let's add a second to make it a + * non-session cookie */ - CURLofft offt; - const char *maxage = valuep; - offt = curlx_strtoofft((*maxage == '\"') ? - &maxage[1] : &maxage[0], NULL, 10, - &co->expires); - switch(offt) { - case CURL_OFFT_FLOW: - /* overflow, used max value */ - co->expires = CURL_OFF_T_MAX; - break; - case CURL_OFFT_INVAL: - /* negative or otherwise bad, expire */ + if(co->expires == 0) co->expires = 1; - break; - case CURL_OFFT_OK: - if(!co->expires) - /* already expired */ - co->expires = 1; - else if(CURL_OFF_T_MAX - now < co->expires) - /* would overflow */ - co->expires = CURL_OFF_T_MAX; - else - co->expires += now; - break; - } + else if(co->expires < 0) + co->expires = 0; } - else if((nlen == 7) && strncasecompare("expires", namep, 7)) { - char date[128]; - if(!co->expires && (vlen < sizeof(date))) { - /* copy the date so that it can be null terminated */ - memcpy(date, valuep, vlen); - date[vlen] = 0; - /* - * Let max-age have priority. - * - * If the date cannot get parsed for whatever reason, the cookie - * will be treated as a session cookie - */ - co->expires = Curl_getdate_capped(date); - - /* - * Session cookies have expires set to 0 so if we get that back - * from the date parser let's add a second to make it a - * non-session cookie - */ - if(co->expires == 0) - co->expires = 1; - else if(co->expires < 0) - co->expires = 0; - } - } - - /* - * Else, this is the second (or more) name we do not know about! - */ - } - else { - /* this is an "illegal" = pair */ } - while(*ptr && ISBLANK(*ptr)) - ptr++; - if(*ptr == ';') - ptr++; - else - break; - } while(1); - - if(!badcookie && !co->domain) { - if(domain) { - /* no domain was given in the header line, set the default */ - co->domain = strdup(domain); - if(!co->domain) - badcookie = TRUE; - } - } - - if(!badcookie && !co->path && path) { - /* - * No path was given in the header line, set the default. Note that the - * passed-in path to this function MAY have a '?' and following part that - * MUST NOT be stored as part of the path. - */ - char *queryp = strchr(path, '?'); - /* - * queryp is where the interesting part of the path ends, so now we - * want to the find the last + * Else, this is the second (or more) name we do not know about! */ - char *endslash; - if(!queryp) - endslash = strrchr(path, '/'); - else - endslash = memrchr(path, '/', (queryp - path)); - if(endslash) { - size_t pathlen = (endslash-path + 1); /* include end slash */ - co->path = Curl_memdup0(path, pathlen); - if(co->path) { - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) - badcookie = TRUE; /* out of memory bad */ - } - else - badcookie = TRUE; - } } - - /* - * If we did not get a cookie name, or a bad one, the this is an illegal - * line so bail out. - */ - if(badcookie || !co->name) { - freecookie(co); - return NULL; + else { + /* this is an "illegal" = pair */ } - data->req.setcookies++; + + while(*ptr && ISBLANK(*ptr)) + ptr++; + if(*ptr == ';') + ptr++; + else + break; + } while(1); + + if(!co->domain && domain) { + /* no domain was given in the header line, set the default */ + co->domain = strdup(domain); + if(!co->domain) + return CERR_OUT_OF_MEMORY; } - else { - /* - * This line is NOT an HTTP header style line, we do offer support for - * reading the odd netscape cookies-file format here - */ - char *ptr; - char *firstptr; - char *tok_buf = NULL; - int fields; + if(!co->path && path) { /* - * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked - * with httpOnly after the domain name are not accessible from javascripts, - * but since curl does not operate at javascript level, we include them - * anyway. In Firefox's cookie files, these lines are preceded with - * #HttpOnly_ and then everything is as usual, so we skip 10 characters of - * the line.. + * No path was given in the header line, set the default. Note that the + * passed-in path to this function MAY have a '?' and following part that + * MUST NOT be stored as part of the path. */ - if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { - lineptr += 10; - co->httponly = TRUE; - } - - if(lineptr[0]=='#') { - /* do not even try the comments */ - free(co); - return NULL; - } - /* strip off the possible end-of-line characters */ - ptr = strchr(lineptr, '\r'); - if(ptr) - *ptr = 0; /* clear it */ - ptr = strchr(lineptr, '\n'); - if(ptr) - *ptr = 0; /* clear it */ - - firstptr = strtok_r((char *)lineptr, "\t", &tok_buf); /* tokenize on TAB */ + char *queryp = strchr(path, '?'); /* - * Now loop through the fields and init the struct we already have - * allocated + * queryp is where the interesting part of the path ends, so now we + * want to the find the last */ - fields = 0; - for(ptr = firstptr; ptr && !badcookie; - ptr = strtok_r(NULL, "\t", &tok_buf), fields++) { - switch(fields) { - case 0: - if(ptr[0]=='.') /* skip preceding dots */ - ptr++; - co->domain = strdup(ptr); - if(!co->domain) - badcookie = TRUE; - break; - case 1: - /* - * flag: A TRUE/FALSE value indicating if all machines within a given - * domain can access the variable. Set TRUE when the cookie says - * .domain.com and to false when the domain is complete www.domain.com - */ - co->tailmatch = strcasecompare(ptr, "TRUE") ? TRUE : FALSE; - break; - case 2: - /* The file format allows the path field to remain not filled in */ - if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { - /* only if the path does not look like a boolean option! */ - co->path = strdup(ptr); - if(!co->path) - badcookie = TRUE; - else { - co->spath = sanitize_cookie_path(co->path); - if(!co->spath) { - badcookie = TRUE; /* out of memory bad */ - } - } - break; - } - /* this does not look like a path, make one up! */ - co->path = strdup("/"); - if(!co->path) - badcookie = TRUE; - co->spath = strdup("/"); + char *endslash; + if(!queryp) + endslash = strrchr(path, '/'); + else + endslash = memrchr(path, '/', (queryp - path)); + if(endslash) { + size_t pathlen = (endslash-path + 1); /* include end slash */ + co->path = Curl_memdup0(path, pathlen); + if(co->path) { + co->spath = sanitize_cookie_path(co->path); if(!co->spath) - badcookie = TRUE; - fields++; /* add a field and fall down to secure */ - FALLTHROUGH(); - case 3: - co->secure = FALSE; - if(strcasecompare(ptr, "TRUE")) { - if(secure || c->running) - co->secure = TRUE; - else - badcookie = TRUE; - } - break; - case 4: - if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) - badcookie = TRUE; - break; - case 5: - co->name = strdup(ptr); - if(!co->name) - badcookie = TRUE; - else { - /* For Netscape file format cookies we check prefix on the name */ - if(strncasecompare("__Secure-", co->name, 9)) - co->prefix |= COOKIE_PREFIX__SECURE; - else if(strncasecompare("__Host-", co->name, 7)) - co->prefix |= COOKIE_PREFIX__HOST; - } - break; - case 6: - co->value = strdup(ptr); - if(!co->value) - badcookie = TRUE; - break; + return CERR_OUT_OF_MEMORY; } - } - if(6 == fields) { - /* we got a cookie with blank contents, fix it */ - co->value = strdup(""); - if(!co->value) - badcookie = TRUE; else - fields++; + return CERR_OUT_OF_MEMORY; } + } - if(!badcookie && (7 != fields)) - /* we did not find the sufficient number of fields */ - badcookie = TRUE; + /* + * If we did not get a cookie name, or a bad one, the this is an illegal + * line so bail out. + */ + if(!co->name) + return CERR_BAD; - if(badcookie) { - freecookie(co); - return NULL; - } + data->req.setcookies++; + return CERR_OK; +} - } +static int +parse_netscape(struct Cookie *co, + struct CookieInfo *ci, + const char *lineptr, + bool secure) /* TRUE if connection is over secure + origin */ +{ + /* + * This line is NOT an HTTP header style line, we do offer support for + * reading the odd netscape cookies-file format here + */ + char *ptr; + char *firstptr; + char *tok_buf = NULL; + int fields; - if(co->prefix & COOKIE_PREFIX__SECURE) { - /* The __Secure- prefix only requires that the cookie be set secure */ - if(!co->secure) { - freecookie(co); - return NULL; - } - } - if(co->prefix & COOKIE_PREFIX__HOST) { - /* - * The __Host- prefix requires the cookie to be secure, have a "/" path - * and not have a domain set. - */ - if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) - ; - else { - freecookie(co); - return NULL; - } + /* + * In 2008, Internet Explorer introduced HTTP-only cookies to prevent XSS + * attacks. Cookies marked httpOnly are not accessible to JavaScript. In + * 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) { + lineptr += 10; + co->httponly = TRUE; } - if(!c->running && /* read from a file */ - c->newsession && /* clean session cookies */ - !co->expires) { /* this is a session cookie since it does not expire! */ - freecookie(co); - return NULL; - } + if(lineptr[0]=='#') + /* do not even try the comments */ + return CERR_COMMENT; + + /* strip off the possible end-of-line characters */ + ptr = strchr(lineptr, '\r'); + if(ptr) + *ptr = 0; /* clear it */ + ptr = strchr(lineptr, '\n'); + if(ptr) + *ptr = 0; /* clear it */ - co->livecookie = c->running; - co->creationtime = ++c->lastct; + /* tokenize on TAB */ + firstptr = Curl_strtok_r((char *)lineptr, "\t", &tok_buf); /* - * Now we have parsed the incoming line, we must now check if this supersedes - * an already existing cookie, which it may if the previous have the same - * domain and path as this. + * Now loop through the fields and init the struct we already have + * allocated */ + fields = 0; + for(ptr = firstptr; ptr; + ptr = Curl_strtok_r(NULL, "\t", &tok_buf), fields++) { + switch(fields) { + case 0: + if(ptr[0]=='.') /* skip preceding dots */ + ptr++; + co->domain = strdup(ptr); + if(!co->domain) + return CERR_OUT_OF_MEMORY; + break; + case 1: + /* + * flag: A TRUE/FALSE value indicating if all machines within a given + * domain can access the variable. Set TRUE when the cookie says + * .domain.com and to false when the domain is complete www.domain.com + */ + co->tailmatch = !!strcasecompare(ptr, "TRUE"); + break; + case 2: + /* The file format allows the path field to remain not filled in */ + if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { + /* only if the path does not look like a boolean option! */ + co->path = strdup(ptr); + if(!co->path) + return CERR_OUT_OF_MEMORY; + else { + co->spath = sanitize_cookie_path(co->path); + if(!co->spath) + return CERR_OUT_OF_MEMORY; + } + break; + } + /* this does not look like a path, make one up! */ + co->path = strdup("/"); + if(!co->path) + return CERR_OUT_OF_MEMORY; + co->spath = strdup("/"); + if(!co->spath) + return CERR_OUT_OF_MEMORY; + fields++; /* add a field and fall down to secure */ + FALLTHROUGH(); + case 3: + co->secure = FALSE; + if(strcasecompare(ptr, "TRUE")) { + if(secure || ci->running) + co->secure = TRUE; + else + return CERR_BAD_SECURE; + } + break; + case 4: + if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) + return CERR_RANGE; + break; + case 5: + co->name = strdup(ptr); + if(!co->name) + return CERR_OUT_OF_MEMORY; + else { + /* For Netscape file format cookies we check prefix on the name */ + if(strncasecompare("__Secure-", co->name, 9)) + co->prefix_secure = TRUE; + else if(strncasecompare("__Host-", co->name, 7)) + co->prefix_host = TRUE; + } + break; + case 6: + co->value = strdup(ptr); + if(!co->value) + return CERR_OUT_OF_MEMORY; + break; + } + } + if(6 == fields) { + /* we got a cookie with blank contents, fix it */ + co->value = strdup(""); + if(!co->value) + return CERR_OUT_OF_MEMORY; + else + fields++; + } - /* at first, remove expired cookies */ - if(!noexpire) - remove_expired(c); + if(7 != fields) + /* we did not find the sufficient number of fields */ + return CERR_FIELDS; + + return CERR_OK; +} +static int +is_public_suffix(struct Curl_easy *data, + struct Cookie *co, + const char *domain) +{ #ifdef USE_LIBPSL /* * Check if the domain is a Public Suffix and if yes, ignore the cookie. We @@ -1051,21 +964,34 @@ Curl_cookie_add(struct Curl_easy *data, if(!acceptable) { infof(data, "cookie '%s' dropped, domain '%s' must not " - "set cookies for '%s'", co->name, domain, co->domain); - freecookie(co); - return NULL; + "set cookies for '%s'", co->name, domain, co->domain); + return CERR_PSL; } } #else + (void)data; + (void)co; + (void)domain; DEBUGF(infof(data, "NO PSL to check set-cookie '%s' for domain=%s in %s", co->name, co->domain, domain)); #endif + return CERR_OK; +} - /* A non-secure cookie may not overlay an existing secure cookie. */ - myhash = cookiehash(co->domain); - clist = c->cookies[myhash]; - while(clist) { - if(strcasecompare(clist->name, co->name)) { +static int +replace_existing(struct Curl_easy *data, + struct Cookie *co, + struct CookieInfo *ci, + bool secure, + bool *replacep) +{ + bool replace_old = FALSE; + struct Curl_llist_node *replace_n = NULL; + struct Curl_llist_node *n; + size_t myhash = cookiehash(co->domain); + for(n = Curl_llist_head(&ci->cookielist[myhash]); n; n = Curl_node_next(n)) { + struct Cookie *clist = Curl_node_elem(n); + if(!strcmp(clist->name, co->name)) { /* the names are identical */ bool matching_domains = FALSE; @@ -1100,13 +1026,12 @@ Curl_cookie_add(struct Curl_easy *data, if(strncasecompare(clist->spath, co->spath, cllen)) { infof(data, "cookie '%s' for domain '%s' dropped, would " "overlay an existing cookie", co->name, co->domain); - freecookie(co); - return NULL; + return CERR_BAD_SECURE; } } } - if(!replace_co && strcasecompare(clist->name, co->name)) { + if(!replace_n && !strcmp(clist->name, co->name)) { /* the names are identical */ if(clist->domain && co->domain) { @@ -1135,62 +1060,137 @@ Curl_cookie_add(struct Curl_easy *data, * was read from a file and thus is not "live". "live" cookies are * preferred so the new cookie is freed. */ - freecookie(co); - return NULL; - } - if(replace_old) { - replace_co = co; - replace_clist = clist; + return CERR_LIVE_WINS; } + if(replace_old) + replace_n = n; } - lastc = clist; - clist = clist->next; } - if(replace_co) { - co = replace_co; - clist = replace_clist; - co->next = clist->next; /* get the next-pointer first */ + if(replace_n) { + struct Cookie *repl = Curl_node_elem(replace_n); /* when replacing, creationtime is kept from old */ - co->creationtime = clist->creationtime; + co->creationtime = repl->creationtime; + + /* unlink the old */ + Curl_node_remove(replace_n); + + /* free the old cookie */ + freecookie(repl); + } + *replacep = replace_old; + return CERR_OK; +} + +/* + * Curl_cookie_add + * + * Add a single cookie line to the cookie keeping object. Be aware that + * sometimes we get an IP-only hostname, and that might also be a numerical + * IPv6 address. + * + * Returns NULL on out of memory or invalid cookie. This is suboptimal, + * as they should be treated separately. + */ +struct Cookie * +Curl_cookie_add(struct Curl_easy *data, + struct CookieInfo *ci, + bool httpheader, /* TRUE if HTTP header-style line */ + bool noexpire, /* if TRUE, skip remove_expired() */ + const char *lineptr, /* first character of the line */ + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is set, + used to get default path for the cookie + unless set */ + bool secure) /* TRUE if connection is over secure origin */ +{ + struct Cookie *co; + size_t myhash; + int rc; + bool replaces = FALSE; + + DEBUGASSERT(data); + DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ + if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) + return NULL; + + /* First, alloc and init a new struct for it */ + co = calloc(1, sizeof(struct Cookie)); + if(!co) + return NULL; /* bail out if we are this low on memory */ + + if(httpheader) + rc = parse_cookie_header(data, co, ci, lineptr, domain, path, secure); + else + rc = parse_netscape(co, ci, lineptr, secure); - /* then free all the old pointers */ - free(clist->name); - free(clist->value); - free(clist->domain); - free(clist->path); - free(clist->spath); + if(rc) + goto fail; - *clist = *co; /* then store all the new data */ + if(co->prefix_secure && !co->secure) + /* The __Secure- prefix only requires that the cookie be set secure */ + goto fail; - free(co); /* free the newly allocated memory */ - co = clist; + if(co->prefix_host) { + /* + * The __Host- prefix requires the cookie to be secure, have a "/" path + * and not have a domain set. + */ + if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) + ; + else + goto fail; } - if(c->running) + if(!ci->running && /* read from a file */ + ci->newsession && /* clean session cookies */ + !co->expires) /* this is a session cookie since it does not expire */ + goto fail; + + co->livecookie = ci->running; + co->creationtime = ++ci->lastct; + + /* + * Now we have parsed the incoming line, we must now check if this supersedes + * an already existing cookie, which it may if the previous have the same + * domain and path as this. + */ + + /* remove expired cookies */ + if(!noexpire) + remove_expired(ci); + + if(is_public_suffix(data, co, domain)) + goto fail; + + if(replace_existing(data, co, ci, secure, &replaces)) + goto fail; + + /* add this cookie to the list */ + myhash = cookiehash(co->domain); + Curl_llist_append(&ci->cookielist[myhash], co, &co->node); + + if(ci->running) /* Only show this when NOT reading the cookies from a file */ infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " "expire %" FMT_OFF_T, - replace_old ? "Replaced":"Added", co->name, co->value, + replaces ? "Replaced":"Added", co->name, co->value, co->domain, co->path, co->expires); - if(!replace_old) { - /* then make the last item point on this new one */ - if(lastc) - lastc->next = co; - else - c->cookies[myhash] = co; - c->numcookies++; /* one more cookie in the jar */ - } + if(!replaces) + ci->numcookies++; /* one more cookie in the jar */ /* * Now that we have added a new cookie to the jar, update the expiration * tracker in case it is the next one to expire. */ - if(co->expires && (co->expires < c->next_expiration)) - c->next_expiration = co->expires; + if(co->expires && (co->expires < ci->next_expiration)) + ci->next_expiration = co->expires; return co; +fail: + freecookie(co); + return NULL; } @@ -1210,28 +1210,30 @@ Curl_cookie_add(struct Curl_easy *data, */ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, const char *file, - struct CookieInfo *inc, + struct CookieInfo *ci, bool newsession) { - struct CookieInfo *c; FILE *handle = NULL; - if(!inc) { + if(!ci) { + int i; + /* we did not get a struct, create one */ - c = calloc(1, sizeof(struct CookieInfo)); - if(!c) + ci = calloc(1, sizeof(struct CookieInfo)); + if(!ci) return NULL; /* failed to get memory */ + + /* This does not use the destructor callback since we want to add + and remove to lists while keeping the cookie struct intact */ + for(i = 0; i < COOKIE_HASH_SIZE; i++) + Curl_llist_init(&ci->cookielist[i], NULL); /* * Initialize the next_expiration time to signal that we do not have enough * information yet. */ - c->next_expiration = CURL_OFF_T_MAX; + ci->next_expiration = CURL_OFF_T_MAX; } - else { - /* we got an already existing one, use that */ - c = inc; - } - c->newsession = newsession; /* new session? */ + ci->newsession = newsession; /* new session? */ if(data) { FILE *fp = NULL; @@ -1247,7 +1249,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, } } - c->running = FALSE; /* this is not running, this is init */ + ci->running = FALSE; /* this is not running, this is init */ if(fp) { struct dynbuf buf; Curl_dyn_init(&buf, MAX_COOKIE_LINE); @@ -1262,7 +1264,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, lineptr++; } - Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); + Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, NULL, TRUE); } Curl_dyn_free(&buf); /* free the line buffer */ @@ -1270,16 +1272,16 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data, * Remove expired cookies from the hash. We must make sure to run this * after reading the file, and not on every cookie. */ - remove_expired(c); + remove_expired(ci); if(handle) fclose(handle); } data->state.cookie_engine = TRUE; } - c->running = TRUE; /* now, we are running */ + ci->running = TRUE; /* now, we are running */ - return c; + return ci; } /* @@ -1334,38 +1336,6 @@ static int cookie_sort_ct(const void *p1, const void *p2) return (c2->creationtime > c1->creationtime) ? 1 : -1; } -#define CLONE(field) \ - do { \ - if(src->field) { \ - d->field = strdup(src->field); \ - if(!d->field) \ - goto fail; \ - } \ - } while(0) - -static struct Cookie *dup_cookie(struct Cookie *src) -{ - struct Cookie *d = calloc(1, sizeof(struct Cookie)); - if(d) { - CLONE(domain); - CLONE(path); - CLONE(spath); - CLONE(name); - CLONE(value); - d->expires = src->expires; - d->tailmatch = src->tailmatch; - d->secure = src->secure; - d->livecookie = src->livecookie; - d->httponly = src->httponly; - d->creationtime = src->creationtime; - } - return d; - -fail: - freecookie(d); - return NULL; -} - /* * Curl_cookie_getlist * @@ -1374,31 +1344,35 @@ static struct Cookie *dup_cookie(struct Cookie *src) * if a secure connection is achieved or not. * * It shall only return cookies that have not expired. + * + * Returns 0 when there is a list returned. Otherwise non-zero. */ -struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, - struct CookieInfo *c, - const char *host, const char *path, - bool secure) +int Curl_cookie_getlist(struct Curl_easy *data, + struct CookieInfo *ci, + const char *host, const char *path, + bool secure, + struct Curl_llist *list) { - struct Cookie *newco; - struct Cookie *co; - struct Cookie *mainco = NULL; size_t matches = 0; bool is_ip; const size_t myhash = cookiehash(host); + struct Curl_llist_node *n; - if(!c || !c->cookies[myhash]) - return NULL; /* no cookie struct or no cookies in the struct */ + Curl_llist_init(list, NULL); + + if(!ci || !Curl_llist_count(&ci->cookielist[myhash])) + return 1; /* no cookie struct or no cookies in the struct */ /* at first, remove expired cookies */ - remove_expired(c); + remove_expired(ci); /* check if host is an IP(v4|v6) address */ is_ip = Curl_host_is_ipnum(host); - co = c->cookies[myhash]; + for(n = Curl_llist_head(&ci->cookielist[myhash]); + n; n = Curl_node_next(n)) { + struct Cookie *co = Curl_node_elem(n); - while(co) { /* if the cookie requires we are secure we must only continue if we are! */ if(co->secure ? secure : TRUE) { @@ -1419,31 +1393,18 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, if(!co->spath || pathmatch(co->spath, path) ) { /* - * and now, we know this is a match and we should create an - * entry for the return-linked-list + * This is a match and we add it to the return-linked-list */ - - newco = dup_cookie(co); - if(newco) { - /* then modify our next */ - newco->next = mainco; - - /* point the main to us */ - mainco = newco; - - matches++; - if(matches >= MAX_COOKIE_SEND_AMOUNT) { - infof(data, "Included max number of cookies (%zu) in request!", - matches); - break; - } + Curl_llist_append(list, co, &co->getnode); + matches++; + if(matches >= MAX_COOKIE_SEND_AMOUNT) { + infof(data, "Included max number of cookies (%zu) in request!", + matches); + break; } - else - goto fail; } } } - co = co->next; } if(matches) { @@ -1460,30 +1421,29 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, if(!array) goto fail; - co = mainco; + n = Curl_llist_head(list); - for(i = 0; co; co = co->next) - array[i++] = co; + for(i = 0; n; n = Curl_node_next(n)) + array[i++] = Curl_node_elem(n); /* now sort the cookie pointers in path length order */ qsort(array, matches, sizeof(struct Cookie *), cookie_sort); /* remake the linked list order according to the new order */ + Curl_llist_destroy(list, NULL); - mainco = array[0]; /* start here */ - for(i = 0; i < matches-1; i++) - array[i]->next = array[i + 1]; - array[matches-1]->next = NULL; /* terminate the list */ + for(i = 0; i < matches; i++) + Curl_llist_append(list, array[i], &array[i]->getnode); free(array); /* remove the temporary data again */ } - return mainco; /* return the new list */ + return 0; /* success */ fail: /* failure, clear up the allocated chain and return NULL */ - Curl_cookie_freelist(mainco); - return NULL; + Curl_llist_destroy(list, NULL); + return 2; /* error */ } /* @@ -1491,30 +1451,21 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, * * Clear all existing cookies and reset the counter. */ -void Curl_cookie_clearall(struct CookieInfo *cookies) +void Curl_cookie_clearall(struct CookieInfo *ci) { - if(cookies) { + if(ci) { unsigned int i; for(i = 0; i < COOKIE_HASH_SIZE; i++) { - Curl_cookie_freelist(cookies->cookies[i]); - cookies->cookies[i] = NULL; + struct Curl_llist_node *n; + for(n = Curl_llist_head(&ci->cookielist[i]); n;) { + struct Cookie *c = Curl_node_elem(n); + struct Curl_llist_node *e = Curl_node_next(n); + Curl_node_remove(n); + freecookie(c); + n = e; + } } - cookies->numcookies = 0; - } -} - -/* - * Curl_cookie_freelist - * - * Free a list of cookies previously returned by Curl_cookie_getlist(); - */ -void Curl_cookie_freelist(struct Cookie *co) -{ - struct Cookie *next; - while(co) { - next = co->next; - freecookie(co); - co = next; + ci->numcookies = 0; } } @@ -1523,39 +1474,26 @@ void Curl_cookie_freelist(struct Cookie *co) * * Free all session cookies in the cookies list. */ -void Curl_cookie_clearsess(struct CookieInfo *cookies) +void Curl_cookie_clearsess(struct CookieInfo *ci) { - struct Cookie *first, *curr, *next, *prev = NULL; unsigned int i; - if(!cookies) + if(!ci) return; for(i = 0; i < COOKIE_HASH_SIZE; i++) { - if(!cookies->cookies[i]) - continue; - - first = curr = prev = cookies->cookies[i]; + struct Curl_llist_node *n = Curl_llist_head(&ci->cookielist[i]); + struct Curl_llist_node *e = NULL; - for(; curr; curr = next) { - next = curr->next; + for(; n; n = e) { + struct Cookie *curr = Curl_node_elem(n); + e = Curl_node_next(n); /* in case the node is removed, get it early */ if(!curr->expires) { - if(first == curr) - first = next; - - if(prev == curr) - prev = next; - else - prev->next = next; - + Curl_node_remove(n); freecookie(curr); - cookies->numcookies--; + ci->numcookies--; } - else - prev = curr; } - - cookies->cookies[i] = first; } } @@ -1564,13 +1502,11 @@ void Curl_cookie_clearsess(struct CookieInfo *cookies) * * Free a "cookie object" previous created with Curl_cookie_init(). */ -void Curl_cookie_cleanup(struct CookieInfo *c) +void Curl_cookie_cleanup(struct CookieInfo *ci) { - if(c) { - unsigned int i; - for(i = 0; i < COOKIE_HASH_SIZE; i++) - Curl_cookie_freelist(c->cookies[i]); - free(c); /* free the base struct as well */ + if(ci) { + Curl_cookie_clearall(ci); + free(ci); /* free the base struct as well */ } } @@ -1616,20 +1552,20 @@ static char *get_netscape_format(const struct Cookie *co) * The function returns non-zero on write failure. */ static CURLcode cookie_output(struct Curl_easy *data, - struct CookieInfo *c, const char *filename) + struct CookieInfo *ci, + const char *filename) { - struct Cookie *co; FILE *out = NULL; bool use_stdout = FALSE; char *tempstore = NULL; CURLcode error = CURLE_OK; - if(!c) + if(!ci) /* no cookie engine alive */ return CURLE_OK; /* at first, remove expired cookies */ - remove_expired(c); + remove_expired(ci); if(!strcmp("-", filename)) { /* use stdout */ @@ -1647,12 +1583,13 @@ static CURLcode cookie_output(struct Curl_easy *data, "# This file was generated by libcurl! Edit at your own risk.\n\n", out); - if(c->numcookies) { + if(ci->numcookies) { unsigned int i; size_t nvalid = 0; struct Cookie **array; + struct Curl_llist_node *n; - array = calloc(1, sizeof(struct Cookie *) * c->numcookies); + array = calloc(1, sizeof(struct Cookie *) * ci->numcookies); if(!array) { error = CURLE_OUT_OF_MEMORY; goto error; @@ -1660,7 +1597,9 @@ static CURLcode cookie_output(struct Curl_easy *data, /* only sort the cookies with a domain property */ for(i = 0; i < COOKIE_HASH_SIZE; i++) { - for(co = c->cookies[i]; co; co = co->next) { + for(n = Curl_llist_head(&ci->cookielist[i]); n; + n = Curl_node_next(n)) { + struct Cookie *co = Curl_node_elem(n); if(!co->domain) continue; array[nvalid++] = co; @@ -1712,15 +1651,17 @@ static struct curl_slist *cookie_list(struct Curl_easy *data) { struct curl_slist *list = NULL; struct curl_slist *beg; - struct Cookie *c; - char *line; unsigned int i; + struct Curl_llist_node *n; if(!data->cookies || (data->cookies->numcookies == 0)) return NULL; for(i = 0; i < COOKIE_HASH_SIZE; i++) { - for(c = data->cookies->cookies[i]; c; c = c->next) { + for(n = Curl_llist_head(&data->cookies->cookielist[i]); n; + n = Curl_node_next(n)) { + struct Cookie *c = Curl_node_elem(n); + char *line; if(!c->domain) continue; line = get_netscape_format(c); diff --git a/lib/cookie.h b/lib/cookie.h index 838d74d82f2bd1..c98f3602e442d9 100644 --- a/lib/cookie.h +++ b/lib/cookie.h @@ -27,20 +27,24 @@ #include +#include "llist.h" + struct Cookie { - struct Cookie *next; /* next in the chain */ - char *name; /* = value */ - char *value; /* name = */ + struct Curl_llist_node node; /* for the main cookie list */ + struct Curl_llist_node getnode; /* for getlist */ + char *name; /* = value */ + char *value; /* name = */ char *path; /* path = which is in Set-Cookie: */ char *spath; /* sanitized cookie path */ - char *domain; /* domain = */ - curl_off_t expires; /* expires = */ - bool tailmatch; /* whether we do tail-matching of the domain name */ - bool secure; /* whether the 'secure' keyword was used */ - bool livecookie; /* updated from a server, not a stored file */ - bool httponly; /* true if the httponly directive is present */ - int creationtime; /* time when the cookie was written */ - unsigned char prefix; /* bitmap fields indicating which prefix are set */ + char *domain; /* domain = */ + curl_off_t expires; /* expires = */ + int creationtime; /* time when the cookie was written */ + BIT(tailmatch); /* tail-match the domain name */ + BIT(secure); /* the 'secure' keyword was used */ + BIT(livecookie); /* updated from a server, not a stored file */ + BIT(httponly); /* the httponly directive is present */ + BIT(prefix_secure); /* secure prefix is set */ + BIT(prefix_host); /* host prefix is set */ }; /* @@ -53,8 +57,8 @@ struct Cookie { #define COOKIE_HASH_SIZE 63 struct CookieInfo { - /* linked list of cookies we know of */ - struct Cookie *cookies[COOKIE_HASH_SIZE]; + /* linked lists of cookies we know of */ + struct Curl_llist cookielist[COOKIE_HASH_SIZE]; curl_off_t next_expiration; /* the next time at which expiration happens */ int numcookies; /* number of cookies in the "jar" */ int lastct; /* last creation-time used in the jar */ @@ -112,10 +116,10 @@ struct Cookie *Curl_cookie_add(struct Curl_easy *data, const char *domain, const char *path, bool secure); -struct Cookie *Curl_cookie_getlist(struct Curl_easy *data, - struct CookieInfo *c, const char *host, - const char *path, bool secure); -void Curl_cookie_freelist(struct Cookie *cookies); +int Curl_cookie_getlist(struct Curl_easy *data, + struct CookieInfo *c, const char *host, + const char *path, bool secure, + struct Curl_llist *list); void Curl_cookie_clearall(struct CookieInfo *cookies); void Curl_cookie_clearsess(struct CookieInfo *cookies); diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c index 44e10e9c9ae9c8..a08caf3328c6bd 100644 --- a/lib/curl_addrinfo.c +++ b/lib/curl_addrinfo.c @@ -252,6 +252,7 @@ Curl_getaddrinfo_ex(const char *nodename, * #define h_addr h_addr_list[0] */ +#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) struct Curl_addrinfo * Curl_he2ai(const struct hostent *he, int port) { @@ -350,19 +351,7 @@ Curl_he2ai(const struct hostent *he, int port) return firstai; } - - -struct namebuff { - struct hostent hostentry; - union { - struct in_addr ina4; -#ifdef USE_IPV6 - struct in6_addr ina6; #endif - } addrentry; - char *h_addr_list[2]; -}; - /* * Curl_ip2addr() @@ -377,71 +366,68 @@ struct Curl_addrinfo * Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port) { struct Curl_addrinfo *ai; - -#if defined(__VMS) && \ - defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64) -#pragma pointer_size save -#pragma pointer_size short -#pragma message disable PTRMISMATCH -#endif - - struct hostent *h; - struct namebuff *buf; - char *addrentry; - char *hoststr; size_t addrsize; + size_t namelen; + struct sockaddr_in *addr; +#ifdef USE_IPV6 + struct sockaddr_in6 *addr6; +#endif DEBUGASSERT(inaddr && hostname); - buf = malloc(sizeof(struct namebuff)); - if(!buf) + namelen = strlen(hostname) + 1; + + if(af == AF_INET) + addrsize = sizeof(struct sockaddr_in); +#ifdef USE_IPV6 + else if(af == AF_INET6) + addrsize = sizeof(struct sockaddr_in6); +#endif + else return NULL; - hoststr = strdup(hostname); - if(!hoststr) { - free(buf); + /* allocate memory to hold the struct, the address and the name */ + ai = calloc(1, sizeof(struct Curl_addrinfo) + addrsize + namelen); + if(!ai) return NULL; - } + /* put the address after the struct */ + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + /* then put the name after the address */ + ai->ai_canonname = (char *)ai->ai_addr + addrsize; + memcpy(ai->ai_canonname, hostname, namelen); + ai->ai_family = af; + ai->ai_socktype = SOCK_STREAM; + ai->ai_addrlen = (curl_socklen_t)addrsize; + /* leave the rest of the struct filled with zero */ switch(af) { case AF_INET: - addrsize = sizeof(struct in_addr); - addrentry = (void *)&buf->addrentry.ina4; - memcpy(addrentry, inaddr, sizeof(struct in_addr)); + addr = (void *)ai->ai_addr; /* storage area for this info */ + + memcpy(&addr->sin_addr, inaddr, sizeof(struct in_addr)); +#ifdef __MINGW32__ + addr->sin_family = (short)af; +#else + addr->sin_family = (CURL_SA_FAMILY_T)af; +#endif + addr->sin_port = htons((unsigned short)port); break; + #ifdef USE_IPV6 case AF_INET6: - addrsize = sizeof(struct in6_addr); - addrentry = (void *)&buf->addrentry.ina6; - memcpy(addrentry, inaddr, sizeof(struct in6_addr)); + addr6 = (void *)ai->ai_addr; /* storage area for this info */ + + memcpy(&addr6->sin6_addr, inaddr, sizeof(struct in6_addr)); +#ifdef __MINGW32__ + addr6->sin6_family = (short)af; +#else + addr6->sin6_family = (CURL_SA_FAMILY_T)af; +#endif + addr6->sin6_port = htons((unsigned short)port); break; #endif - default: - free(hoststr); - free(buf); - return NULL; } - h = &buf->hostentry; - h->h_name = hoststr; - h->h_aliases = NULL; - h->h_addrtype = (short)af; - h->h_length = (short)addrsize; - h->h_addr_list = &buf->h_addr_list[0]; - h->h_addr_list[0] = addrentry; - h->h_addr_list[1] = NULL; /* terminate list of entries */ - -#if defined(__VMS) && \ - defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64) -#pragma pointer_size restore -#pragma message enable PTRMISMATCH -#endif - - ai = Curl_he2ai(h, port); - - free(hoststr); - free(buf); - return ai; } diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h index 9ceac997da512d..2303e95e314ea9 100644 --- a/lib/curl_addrinfo.h +++ b/lib/curl_addrinfo.h @@ -71,8 +71,10 @@ Curl_getaddrinfo_ex(const char *nodename, struct Curl_addrinfo **result); #endif +#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) struct Curl_addrinfo * Curl_he2ai(const struct hostent *he, int port); +#endif struct Curl_addrinfo * Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port); diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index dd806da9bca3bb..1ac59ff057fd22 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -55,7 +55,7 @@ /* disables negotiate authentication */ #cmakedefine CURL_DISABLE_NEGOTIATE_AUTH 1 -/* disables AWS-SIG4 */ +/* disables aws-sigv4 */ #cmakedefine CURL_DISABLE_AWS 1 /* disables DICT */ @@ -219,6 +219,9 @@ /* Define to 1 if you have the `closesocket' function. */ #cmakedefine HAVE_CLOSESOCKET 1 +/* Define to 1 if you have the `CloseSocket' function. */ +#cmakedefine HAVE_CLOSESOCKET_CAMEL 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_DIRENT_H 1 @@ -243,9 +246,6 @@ /* Define to 1 if you have the fseeko declaration. */ #cmakedefine HAVE_DECL_FSEEKO 1 -/* Define to 1 if you have the _fseeki64 function. */ -#cmakedefine HAVE__FSEEKI64 1 - /* Define to 1 if you have the ftruncate function. */ #cmakedefine HAVE_FTRUNCATE 1 @@ -318,9 +318,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_GSSAPI_GSSAPI_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_GSSAPI_GSSAPI_KRB5_H 1 - /* if you have the GNU gssapi libraries */ #cmakedefine HAVE_GSSGNU 1 @@ -409,6 +406,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_IN_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_NETINET_IN6_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_NETINET_TCP_H 1 @@ -481,6 +481,9 @@ /* Define to 1 if you have the `setmode' function. */ #cmakedefine HAVE_SETMODE 1 +/* Define to 1 if you have the `_setmode' function. */ +#cmakedefine HAVE__SETMODE 1 + /* Define to 1 if you have the `setrlimit' function. */ #cmakedefine HAVE_SETRLIMIT 1 @@ -508,6 +511,9 @@ /* Define to 1 if you have the `socket' function. */ #cmakedefine HAVE_SOCKET 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_PROTO_BSDSOCKET_H 1 + /* Define to 1 if you have the socketpair function. */ #cmakedefine HAVE_SOCKETPAIR 1 @@ -626,7 +632,7 @@ #cmakedefine NEED_REENTRANT 1 /* cpu-machine-OS */ -#cmakedefine OS ${OS} +#cmakedefine CURL_OS ${CURL_OS} /* Name of package */ #cmakedefine PACKAGE ${PACKAGE} @@ -712,6 +718,9 @@ ${SIZEOF_TIME_T_CODE} /* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */ #cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1 +/* if wolfSSL has the wolfSSL_BIO_new function. */ +#cmakedefine HAVE_WOLFSSL_BIO 1 + /* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */ #cmakedefine HAVE_WOLFSSL_FULL_BIO 1 diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index c6fe1256b2c055..b7e774cea018ab 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -35,7 +35,7 @@ #include "memdebug.h" #if defined(__GNUC__) -#define CURL_ALIGN8 __attribute__ ((aligned(8))) +#define CURL_ALIGN8 __attribute__((aligned(8))) #else #define CURL_ALIGN8 #endif diff --git a/lib/curl_hmac.h b/lib/curl_hmac.h index 7a5387a9488270..ed5035ca632e6e 100644 --- a/lib/curl_hmac.h +++ b/lib/curl_hmac.h @@ -32,30 +32,28 @@ #define HMAC_MD5_LENGTH 16 -typedef CURLcode (* HMAC_hinit_func)(void *context); -typedef void (* HMAC_hupdate_func)(void *context, - const unsigned char *data, - unsigned int len); -typedef void (* HMAC_hfinal_func)(unsigned char *result, void *context); - +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); /* Per-hash function HMAC parameters. */ struct HMAC_params { - HMAC_hinit_func - hmac_hinit; /* Initialize context procedure. */ - HMAC_hupdate_func hmac_hupdate; /* Update context with data. */ - HMAC_hfinal_func hmac_hfinal; /* Get final result procedure. */ - unsigned int hmac_ctxtsize; /* Context structure size. */ - unsigned int hmac_maxkeylen; /* Maximum key length (bytes). */ - unsigned int hmac_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. */ struct HMAC_context { - const struct HMAC_params *hmac_hash; /* Hash function definition. */ - void *hmac_hashctxt1; /* Hash function context 1. */ - void *hmac_hashctxt2; /* Hash function context 2. */ + const struct HMAC_params *hash; /* Hash function definition. */ + void *hashctxt1; /* Hash function context 1. */ + void *hashctxt2; /* Hash function context 2. */ }; diff --git a/lib/curl_md5.h b/lib/curl_md5.h index 61671c306a643f..ec27503b144440 100644 --- a/lib/curl_md5.h +++ b/lib/curl_md5.h @@ -31,11 +31,11 @@ #define MD5_DIGEST_LEN 16 -typedef CURLcode (* Curl_MD5_init_func)(void *context); -typedef void (* Curl_MD5_update_func)(void *context, - const unsigned char *data, - unsigned int len); -typedef void (* Curl_MD5_final_func)(unsigned char *result, void *context); +typedef CURLcode (*Curl_MD5_init_func)(void *context); +typedef void (*Curl_MD5_update_func)(void *context, + const unsigned char *data, + unsigned int len); +typedef void (*Curl_MD5_final_func)(unsigned char *result, void *context); struct MD5_params { Curl_MD5_init_func md5_init_func; /* Initialize context procedure */ @@ -50,8 +50,8 @@ struct MD5_context { void *md5_hashctx; /* Hash function context */ }; -extern const struct MD5_params Curl_DIGEST_MD5[1]; -extern const struct HMAC_params Curl_HMAC_MD5[1]; +extern const struct MD5_params Curl_DIGEST_MD5; +extern const struct HMAC_params Curl_HMAC_MD5; CURLcode Curl_md5it(unsigned char *output, const unsigned char *input, const size_t len); diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index cb8e78a9248416..3cc885e03bdc46 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -528,7 +528,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen, ascii_uppercase_to_unicode_le(identity, user, userlen); ascii_to_unicode_le(identity + (userlen << 1), domain, domlen); - result = Curl_hmacit(Curl_HMAC_MD5, ntlmhash, 16, identity, identity_len, + result = Curl_hmacit(&Curl_HMAC_MD5, ntlmhash, 16, identity, identity_len, ntlmv2hash); free(identity); @@ -613,7 +613,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash, /* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */ memcpy(ptr + 8, &ntlm->nonce[0], 8); - result = Curl_hmacit(Curl_HMAC_MD5, ntlmv2hash, HMAC_MD5_LENGTH, ptr + 8, + result = Curl_hmacit(&Curl_HMAC_MD5, ntlmv2hash, HMAC_MD5_LENGTH, ptr + 8, NTLMv2_BLOB_LEN + 8, hmac_output); if(result) { free(ptr); @@ -656,7 +656,7 @@ CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash, memcpy(&data[0], challenge_server, 8); memcpy(&data[8], challenge_client, 8); - result = Curl_hmacit(Curl_HMAC_MD5, ntlmv2hash, 16, &data[0], 16, + result = Curl_hmacit(&Curl_HMAC_MD5, ntlmv2hash, 16, &data[0], 16, hmac_output); if(result) return result; diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 1c715fd4fee684..f88c5f12aa5736 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -33,7 +33,7 @@ /* FIXME: Delete this once the warnings have been fixed. */ #if !defined(CURL_WARN_SIGN_CONVERSION) -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic ignored "-Wsign-conversion" #endif #endif @@ -43,7 +43,7 @@ #include <_mingw.h> #endif -/* Workaround for Homebrew gcc 12.4.0, 13.3.0, 14.1.0 and newer (as of 14.1.0) +/* Workaround for Homebrew gcc 12.4.0, 13.3.0, 14.1.0, 14.2.0 (initial build) that started advertising the `availability` attribute, which then gets used by Apple SDK, but, in a way incompatible with gcc, resulting in misc errors inside SDK headers, e.g.: @@ -51,13 +51,16 @@ definition error: expected ',' or '}' before Followed by missing declarations. - Fix it by overriding the built-in feature-check macro used by the headers - to enable the problematic attributes. This makes the feature check fail. */ -#if defined(__APPLE__) && \ - !defined(__clang__) && \ - defined(__GNUC__) && __GNUC__ >= 12 && \ + Work it around by overriding the built-in feature-check macro used by the + headers to enable the problematic attributes. This makes the feature check + fail. Fixed in 14.2.0_1. Disable the workaround if the fix is detected. */ +#if defined(__APPLE__) && !defined(__clang__) && defined(__GNUC__) && \ defined(__has_attribute) -#define availability curl_pp_attribute_disabled +# if !defined(__has_feature) +# define availability curl_pp_attribute_disabled +# elif !__has_feature(attribute_availability) +# define availability curl_pp_attribute_disabled +# endif #endif #if defined(__APPLE__) diff --git a/lib/curl_sha256.h b/lib/curl_sha256.h index c3cf00a2177cb5..00e5b74c58dc12 100644 --- a/lib/curl_sha256.h +++ b/lib/curl_sha256.h @@ -31,7 +31,7 @@ #include #include "curl_hmac.h" -extern const struct HMAC_params Curl_HMAC_SHA256[1]; +extern const struct HMAC_params Curl_HMAC_SHA256; #ifndef CURL_SHA256_DIGEST_LENGTH #define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */ diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 8f1be07e7d4786..a3a107a4dc0ce0 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -365,7 +365,7 @@ static CURLcode trc_opt(const char *config) if(!tmp) return CURLE_OUT_OF_MEMORY; - token = strtok_r(tmp, ", ", &tok_buf); + token = Curl_strtok_r(tmp, ", ", &tok_buf); while(token) { switch(*token) { case '-': @@ -391,7 +391,7 @@ static CURLcode trc_opt(const char *config) else trc_apply_level_by_name(token, lvl); - token = strtok_r(NULL, ", ", &tok_buf); + token = Curl_strtok_r(NULL, ", ", &tok_buf); } free(tmp); return CURLE_OK; diff --git a/lib/doh.c b/lib/doh.c index 5fff66bdcf2043..8769372e0be6f6 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -180,7 +180,7 @@ UNITTEST DOHcode doh_req_encode(const char *host, } static size_t -doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp) +doh_write_cb(char *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct dynbuf *mem = (struct dynbuf *)userp; @@ -238,14 +238,14 @@ static int doh_done(struct Curl_easy *doh, CURLcode result) return 0; } -#define ERROR_CHECK_SETOPT(x,y) \ -do { \ - result = curl_easy_setopt(doh, x, y); \ - if(result && \ - result != CURLE_NOT_BUILT_IN && \ - result != CURLE_UNKNOWN_OPTION) \ - goto error; \ -} while(0) +#define ERROR_CHECK_SETOPT(x,y) \ + do { \ + result = curl_easy_setopt((CURL *)doh, x, y); \ + if(result && \ + result != CURLE_NOT_BUILT_IN && \ + result != CURLE_UNKNOWN_OPTION) \ + goto error; \ + } while(0) static CURLcode doh_run_probe(struct Curl_easy *data, struct doh_probe *p, DNStype dnstype, @@ -301,7 +301,7 @@ static CURLcode doh_run_probe(struct Curl_easy *data, ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS); #endif ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms); - ERROR_CHECK_SETOPT(CURLOPT_SHARE, data->share); + ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share); if(data->set.err && data->set.err != stderr) ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err); if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) @@ -847,11 +847,9 @@ static void doh_show(struct Curl_easy *data, } else if(a->type == DNS_TYPE_AAAA) { int j; - char buffer[128]; - char *ptr; - size_t len; - len = msnprintf(buffer, 128, "[DoH] AAAA: "); - ptr = &buffer[len]; + char buffer[128] = "[DoH] AAAA: "; + size_t len = strlen(buffer); + char *ptr = &buffer[len]; len = sizeof(buffer) - len; for(j = 0; j < 16; j += 2) { size_t l; diff --git a/lib/easy.c b/lib/easy.c index 95442a771b3d0b..ac8fab34220d9b 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -347,7 +347,7 @@ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, * curl_easy_init() is the external interface to alloc, setup and init an * easy handle that is returned. If anything goes wrong, NULL is returned. */ -struct Curl_easy *curl_easy_init(void) +CURL *curl_easy_init(void) { CURLcode result; struct Curl_easy *data; @@ -398,9 +398,9 @@ struct events { * Callback that gets called with a new value when the timeout should be * updated. */ -static int events_timer(struct Curl_multi *multi, /* multi handle */ +static int events_timer(CURLM *multi, /* multi handle */ long timeout_ms, /* see above */ - void *userp) /* private callback pointer */ + void *userp) /* private callback pointer */ { struct events *ev = userp; (void)multi; @@ -449,7 +449,7 @@ static short socketcb2poll(int pollmask) * Callback that gets called with information about socket activity to * monitor. */ -static int events_socket(struct Curl_easy *easy, /* easy handle */ +static int events_socket(CURL *easy, /* easy handle */ curl_socket_t s, /* socket */ int what, /* see above */ void *userp, /* private callback @@ -461,6 +461,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ struct socketmonitor *m; struct socketmonitor *prev = NULL; bool found = FALSE; + struct Curl_easy *data = easy; #if defined(CURL_DISABLE_VERBOSE_STRINGS) (void) easy; @@ -479,13 +480,13 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ else ev->list = nxt; free(m); - infof(easy, "socket cb: socket %" FMT_SOCKET_T " REMOVED", s); + infof(data, "socket cb: socket %" FMT_SOCKET_T " REMOVED", s); } else { /* The socket 's' is already being monitored, update the activity mask. Convert from libcurl bitmask to the poll one. */ m->socket.events = socketcb2poll(what); - infof(easy, "socket cb: socket %" FMT_SOCKET_T + infof(data, "socket cb: socket %" FMT_SOCKET_T " UPDATED as %s%s", s, (what&CURL_POLL_IN) ? "IN" : "", (what&CURL_POLL_OUT) ? "OUT" : ""); @@ -499,7 +500,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ if(!found) { if(what == CURL_POLL_REMOVE) { /* should not happen if our logic is correct, but is no drama. */ - DEBUGF(infof(easy, "socket cb: asked to REMOVE socket %" + DEBUGF(infof(data, "socket cb: asked to REMOVE socket %" FMT_SOCKET_T "but not present!", s)); DEBUGASSERT(0); } @@ -511,7 +512,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */ m->socket.events = socketcb2poll(what); m->socket.revents = 0; ev->list = m; - infof(easy, "socket cb: socket %" FMT_SOCKET_T " ADDED as %s%s", s, + infof(data, "socket cb: socket %" FMT_SOCKET_T " ADDED as %s%s", s, (what&CURL_POLL_IN) ? "IN" : "", (what&CURL_POLL_OUT) ? "OUT" : ""); } @@ -809,7 +810,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) * curl_easy_perform() is the external interface that performs a blocking * transfer as previously setup. */ -CURLcode curl_easy_perform(struct Curl_easy *data) +CURLcode curl_easy_perform(CURL *data) { return easy_perform(data, FALSE); } @@ -830,8 +831,9 @@ CURLcode curl_easy_perform_ev(struct Curl_easy *data) * curl_easy_cleanup() is the external interface to cleaning/freeing the given * easy handle. */ -void curl_easy_cleanup(struct Curl_easy *data) +void curl_easy_cleanup(CURL *ptr) { + struct Curl_easy *data = ptr; if(GOOD_EASY_HANDLE(data)) { SIGPIPE_VARIABLE(pipe_st); sigpipe_ignore(data, &pipe_st); @@ -845,7 +847,7 @@ void curl_easy_cleanup(struct Curl_easy *data) * information from a performed transfer and similar. */ #undef curl_easy_getinfo -CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...) +CURLcode curl_easy_getinfo(CURL *data, CURLINFO info, ...) { va_list arg; void *paramp; @@ -919,8 +921,9 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) * given input easy handle. The returned handle will be a new working handle * with all options set exactly as the input source handle. */ -struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) +CURL *curl_easy_duphandle(CURL *d) { + struct Curl_easy *data = d; struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy)); if(!outcurl) goto fail; @@ -937,6 +940,7 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) goto fail; Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); + Curl_netrc_init(&outcurl->state.netrc); /* the connection pool is setup on demand */ outcurl->state.lastconnect_id = -1; @@ -1066,8 +1070,9 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) * curl_easy_reset() is an external interface that allows an app to re- * initialize a session handle to the default values. */ -void curl_easy_reset(struct Curl_easy *data) +void curl_easy_reset(CURL *d) { + struct Curl_easy *data = d; Curl_req_hard_reset(&data->req, data); /* zero out UserDefined data: */ @@ -1107,7 +1112,7 @@ void curl_easy_reset(struct Curl_easy *data) * NOTE: This is one of few API functions that are allowed to be called from * within a callback. */ -CURLcode curl_easy_pause(struct Curl_easy *data, int action) +CURLcode curl_easy_pause(CURL *d, int action) { struct SingleRequest *k; CURLcode result = CURLE_OK; @@ -1115,6 +1120,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action) int newstate; bool recursive = FALSE; bool keep_changed, unpause_read, not_all_paused; + struct Curl_easy *data = d; if(!GOOD_EASY_HANDLE(data) || !data->conn) /* crazy input, do not continue */ @@ -1218,12 +1224,12 @@ static CURLcode easy_connection(struct Curl_easy *data, * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. * Returns CURLE_OK on success, error code on error. */ -CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, - size_t *n) +CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n) { CURLcode result; ssize_t n1; struct connectdata *c; + struct Curl_easy *data = d; if(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; @@ -1301,11 +1307,11 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, * Sends data over the connected socket. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, - size_t buflen, size_t *n) +CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n) { size_t written = 0; CURLcode result; + struct Curl_easy *data = d; if(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; @@ -1317,8 +1323,9 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, /* * Performs connection upkeep for the given session handle. */ -CURLcode curl_easy_upkeep(struct Curl_easy *data) +CURLcode curl_easy_upkeep(CURL *d) { + struct Curl_easy *data = d; /* Verify that we got an easy handle we can work with. */ if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; diff --git a/lib/escape.c b/lib/escape.c index fda6a3a12abf97..eaad6d33ad439b 100644 --- a/lib/escape.c +++ b/lib/escape.c @@ -29,6 +29,8 @@ #include +struct Curl_easy; + #include "urldata.h" #include "warnless.h" #include "escape.h" @@ -53,7 +55,7 @@ char *curl_unescape(const char *string, int length) /* Escapes for URL the given unescaped string of given length. * 'data' is ignored since 7.82.0. */ -char *curl_easy_escape(struct Curl_easy *data, const char *string, +char *curl_easy_escape(CURL *data, const char *string, int inlength) { size_t length; @@ -176,7 +178,7 @@ CURLcode Curl_urldecode(const char *string, size_t length, * If olen == NULL, no output length is stored. * 'data' is ignored since 7.82.0. */ -char *curl_easy_unescape(struct Curl_easy *data, const char *string, +char *curl_easy_unescape(CURL *data, const char *string, int length, int *olen) { char *str = NULL; diff --git a/lib/formdata.c b/lib/formdata.c index a0d5a205827e78..7ea7a8f396b48a 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -26,6 +26,8 @@ #include +struct Curl_easy; + #include "formdata.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) @@ -791,7 +793,7 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) /* wrap call to fseeko so it matches the calling convention of callback */ static int fseeko_wrapper(void *stream, curl_off_t offset, int whence) { -#if defined(HAVE__FSEEKI64) +#if defined(_WIN32) && defined(USE_WIN32_LARGE_FILES) return _fseeki64(stream, (__int64)offset, whence); #elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO) return fseeko(stream, (off_t)offset, whence); @@ -812,7 +814,7 @@ static int fseeko_wrapper(void *stream, curl_off_t offset, int whence) * a NULL pointer in the 'data' argument. */ -CURLcode Curl_getformdata(struct Curl_easy *data, +CURLcode Curl_getformdata(CURL *data, curl_mimepart *finalform, struct curl_httppost *post, curl_read_callback fread_func) diff --git a/lib/formdata.h b/lib/formdata.h index 2ed96ffcf6406e..0e35e1892bf044 100644 --- a/lib/formdata.h +++ b/lib/formdata.h @@ -49,7 +49,7 @@ struct FormInfo { bool showfilename_alloc; }; -CURLcode Curl_getformdata(struct Curl_easy *data, +CURLcode Curl_getformdata(CURL *data, curl_mimepart *, struct curl_httppost *post, curl_read_callback fread_func); diff --git a/lib/ftp.c b/lib/ftp.c index 4d5d956593d41e..16ab0af0de15ba 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -302,10 +302,10 @@ static void close_secondarysocket(struct Curl_easy *data) * requests on files respond with headers passed to the client/stdout that * looked like HTTP ones. * - * This approach is not very elegant, it causes confusion and is error-prone. - * It is subject for removal at the next (or at least a future) soname bump. - * Until then you can test the effects of the removal by undefining the - * following define named CURL_FTP_HTTPSTYLE_HEAD. + * This approach is not elegant, it causes confusion and is error-prone. It is + * subject for removal at the next (or at least a future) soname bump. Until + * then you can test the effects of the removal by undefining the following + * define named CURL_FTP_HTTPSTYLE_HEAD. */ #define CURL_FTP_HTTPSTYLE_HEAD 1 @@ -419,138 +419,19 @@ static const struct Curl_cwtype ftp_cw_lc = { #endif /* CURL_PREFER_LF_LINEENDS */ /*********************************************************************** * - * AcceptServerConnect() - * - * After connection request is received from the server this function is - * called to accept the connection and close the listening socket - * - */ -static CURLcode AcceptServerConnect(struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[SECONDARYSOCKET]; - curl_socket_t s = CURL_SOCKET_BAD; -#ifdef USE_IPV6 - struct Curl_sockaddr_storage add; -#else - struct sockaddr_in add; -#endif - curl_socklen_t size = (curl_socklen_t) sizeof(add); - CURLcode result; - - if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) { - size = sizeof(add); - - s = accept(sock, (struct sockaddr *) &add, &size); - } - - if(CURL_SOCKET_BAD == s) { - failf(data, "Error accept()ing server connect"); - return CURLE_FTP_PORT_FAILED; - } - infof(data, "Connection accepted from server"); - /* when this happens within the DO state it is important that we mark us as - not needing DO_MORE anymore */ - conn->bits.do_more = FALSE; - - (void)curlx_nonblock(s, TRUE); /* enable non-blocking */ - /* Replace any filter on SECONDARY with one listening on this socket */ - result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s); - if(result) { - sclose(s); - return result; - } - - if(data->set.fsockopt) { - int error = 0; - - /* activate callback for setting socket options */ - Curl_set_in_callback(data, TRUE); - error = data->set.fsockopt(data->set.sockopt_client, - s, - CURLSOCKTYPE_ACCEPT); - Curl_set_in_callback(data, FALSE); - - if(error) { - close_secondarysocket(data); - return CURLE_ABORTED_BY_CALLBACK; - } - } - - return CURLE_OK; - -} - -/* - * ftp_timeleft_accept() returns the amount of milliseconds left allowed for - * waiting server to connect. If the value is negative, the timeout time has - * already elapsed. - * - * The start time is stored in progress.t_acceptdata - as set with - * Curl_pgrsTime(..., TIMER_STARTACCEPT); - * - */ -static timediff_t ftp_timeleft_accept(struct Curl_easy *data) -{ - timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT; - timediff_t other; - struct curltime now; - - if(data->set.accepttimeout > 0) - timeout_ms = data->set.accepttimeout; - - now = Curl_now(); - - /* check if the generic timeout possibly is set shorter */ - other = Curl_timeleft(data, &now, FALSE); - if(other && (other < timeout_ms)) - /* note that this also works fine for when other happens to be negative - due to it already having elapsed */ - timeout_ms = other; - else { - /* subtract elapsed time */ - timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata); - if(!timeout_ms) - /* avoid returning 0 as that means no timeout! */ - return -1; - } - - return timeout_ms; -} - - -/*********************************************************************** - * - * ReceivedServerConnect() - * - * After allowing server to connect to us from data port, this function - * checks both data connection for connection establishment and ctrl - * connection for a negative response regarding a failure in connecting + * ftp_check_ctrl_on_data_wait() * */ -static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received) +static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data) { struct connectdata *conn = data->conn; curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET]; - curl_socket_t data_sock = conn->sock[SECONDARYSOCKET]; struct ftp_conn *ftpc = &conn->proto.ftpc; struct pingpong *pp = &ftpc->pp; - int socketstate = 0; - timediff_t timeout_ms; ssize_t nread; int ftpcode; bool response = FALSE; - *received = FALSE; - - timeout_ms = ftp_timeleft_accept(data); - infof(data, "Checking for server connect"); - if(timeout_ms < 0) { - /* if a timeout was already reached, bail out */ - failf(data, "Accept timeout occurred while waiting server connect"); - return CURLE_FTP_ACCEPT_TIMEOUT; - } - /* First check whether there is a cached response from server */ if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) { /* Data connection could not be established, let's return */ @@ -562,26 +443,22 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received) if(pp->overflow) /* there is pending control data still in the buffer to read */ response = TRUE; - else - socketstate = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0); - - /* see if the connection request is already here */ - switch(socketstate) { - case -1: /* error */ - /* let's die here */ - failf(data, "Error while waiting for server connect"); - return CURLE_FTP_ACCEPT_FAILED; - case 0: /* Server connect is not received yet */ - break; /* loop */ - default: - if(socketstate & CURL_CSELECT_IN2) { - infof(data, "Ready to accept data connection from server"); - *received = TRUE; + else { + int socketstate = Curl_socket_check(ctrl_sock, CURL_SOCKET_BAD, + CURL_SOCKET_BAD, 0); + /* see if the connection request is already here */ + switch(socketstate) { + case -1: /* error */ + /* let's die here */ + failf(data, "Error while waiting for server connect"); + return CURLE_FTP_ACCEPT_FAILED; + default: + if(socketstate & CURL_CSELECT_IN) + response = TRUE; + break; } - else if(socketstate & CURL_CSELECT_IN) - response = TRUE; - break; } + if(response) { infof(data, "Ctrl conn has data while waiting for data conn"); if(pp->overflow > 3) { @@ -600,7 +477,6 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received) noticed. Leave the 226 in there and use this as a trigger to read the data socket. */ infof(data, "Got 226 before data activity"); - *received = TRUE; return CURLE_OK; } } @@ -619,7 +495,6 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received) return CURLE_OK; } - /*********************************************************************** * * InitiateTransfer() @@ -635,12 +510,6 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) bool connected; CURL_TRC_FTP(data, "InitiateTransfer()"); - if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && - !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { - result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); - if(result) - return result; - } result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected); if(result || !connected) return result; @@ -669,60 +538,6 @@ static CURLcode InitiateTransfer(struct Curl_easy *data) return CURLE_OK; } -/*********************************************************************** - * - * AllowServerConnect() - * - * When we have issue the PORT command, we have told the server to connect to - * us. This function checks whether data connection is established if so it is - * accepted. - * - */ -static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected) -{ - timediff_t timeout_ms; - CURLcode result = CURLE_OK; - - *connected = FALSE; - infof(data, "Preparing for accepting server on data port"); - - /* Save the time we start accepting server connect */ - Curl_pgrsTime(data, TIMER_STARTACCEPT); - - timeout_ms = ftp_timeleft_accept(data); - if(timeout_ms < 0) { - /* if a timeout was already reached, bail out */ - failf(data, "Accept timeout occurred while waiting server connect"); - result = CURLE_FTP_ACCEPT_TIMEOUT; - goto out; - } - - /* see if the connection request is already here */ - result = ReceivedServerConnect(data, connected); - if(result) - goto out; - - if(*connected) { - result = AcceptServerConnect(data); - if(result) - goto out; - - result = InitiateTransfer(data); - if(result) - goto out; - } - else { - /* Add timeout to multi handle and break out of the loop */ - Curl_expire(data, data->set.accepttimeout ? - data->set.accepttimeout : DEFAULT_ACCEPT_TIMEOUT, - EXPIRE_FTP_ACCEPT); - } - -out: - CURL_TRC_FTP(data, "AllowServerConnect() -> %d", result); - return result; -} - static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn, char *line, size_t len, int *code) { @@ -1306,12 +1121,6 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, conn->bits.ftp_use_eprt = TRUE; #endif - /* Replace any filter on SECONDARY with one listening on this socket */ - result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); - if(result) - goto out; - portsock = CURL_SOCKET_BAD; /* now held in filter */ - for(; fcmd != DONE; fcmd++) { if(!conn->bits.ftp_use_eprt && (EPRT == fcmd)) @@ -1384,9 +1193,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* store which command was sent */ ftpc->count1 = fcmd; - ftp_state(data, FTP_PORT); + /* Replace any filter on SECONDARY with one listening on this socket */ + result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock); + if(!result) + portsock = CURL_SOCKET_BAD; /* now held in filter */ + out: /* If we looked up a dns_entry, now is the time to safely release it */ if(dns_entry) @@ -1394,6 +1207,18 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, if(result) { ftp_state(data, FTP_STOP); } + else { + /* successfully setup the list socket filter. Do we need more? */ + if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port && + !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) { + result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET); + } + data->conn->bits.do_more = FALSE; + Curl_pgrsTime(data, TIMER_STARTACCEPT); + Curl_expire(data, data->set.accepttimeout ? + data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, + EXPIRE_FTP_ACCEPT); + } if(portsock != CURL_SOCKET_BAD) Curl_socket_close(data, conn, portsock); return result; @@ -2217,10 +2042,10 @@ static CURLcode client_write_header(struct Curl_easy *data, * headers from CONNECT should not automatically be part of the * output. */ CURLcode result; - int save = data->set.include_header; + bool save = data->set.include_header; data->set.include_header = TRUE; result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen); - data->set.include_header = save ? TRUE : FALSE; + data->set.include_header = save; return result; } @@ -2565,21 +2390,21 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data, /* PORT means we are now awaiting the server to connect to us. */ if(data->set.ftp_use_port) { + struct ftp_conn *ftpc = &conn->proto.ftpc; bool connected; ftp_state(data, FTP_STOP); /* no longer in STOR state */ - result = AllowServerConnect(data, &connected); + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); if(result) return result; if(!connected) { - struct ftp_conn *ftpc = &conn->proto.ftpc; infof(data, "Data conn was not available immediately"); ftpc->wait_data_conn = TRUE; + return ftp_check_ctrl_on_data_wait(data); } - - return CURLE_OK; + ftpc->wait_data_conn = FALSE; } return InitiateTransfer(data); } @@ -2629,10 +2454,10 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, !data->set.ignorecl && (ftp->downloadsize < 1)) { /* - * It seems directory listings either do not show the size or very - * often uses size 0 anyway. ASCII transfers may very well turn out - * that the transferred amount of data is not the same as this line - * tells, why using this number in those cases only confuses us. + * It seems directory listings either do not show the size or often uses + * size 0 anyway. ASCII transfers may cause that the transferred amount + * of data is not the same as this line tells, why using this number in + * those cases only confuses us. * * Example D above makes this parsing a little tricky */ char *bytes; @@ -2679,21 +2504,22 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data, conn->proto.ftpc.retr_size_saved = size; if(data->set.ftp_use_port) { + struct ftp_conn *ftpc = &conn->proto.ftpc; bool connected; - result = AllowServerConnect(data, &connected); + result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); if(result) return result; if(!connected) { - struct ftp_conn *ftpc = &conn->proto.ftpc; infof(data, "Data conn was not available immediately"); ftp_state(data, FTP_STOP); ftpc->wait_data_conn = TRUE; + return ftp_check_ctrl_on_data_wait(data); } + ftpc->wait_data_conn = FALSE; } - else - return InitiateTransfer(data); + return InitiateTransfer(data); } else { if((instate == FTP_LIST) && (ftpcode == 450)) { @@ -2967,7 +2793,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data, if(ftpcode/100 == 2) /* We have enabled SSL for the data connection! */ conn->bits.ftp_use_data_ssl = - (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE; + (data->set.use_ssl != CURLUSESSL_CONTROL); /* FTP servers typically responds with 500 if they decide to reject our 'P' request */ else if(data->set.use_ssl > CURLUSESSL_CONTROL) @@ -3284,7 +3110,7 @@ static CURLcode ftp_multi_statemach(struct Curl_easy *data, /* Check for the state outside of the Curl_socket_check() return code checks since at times we are in fact already in this state when this function gets called. */ - *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE; + *done = (ftpc->state == FTP_STOP); return result; } @@ -3718,20 +3544,25 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) * complete */ struct FTP *ftp = NULL; - /* if the second connection is not done yet, wait for it to have - * connected to the remote host. When using proxy tunneling, this - * means the tunnel needs to have been establish. However, we - * can not expect the remote host to talk to us in any way yet. - * So, when using ftps: the SSL handshake will not start until we - * tell the remote server that we are there. */ + /* if the second connection has been set up, try to connect it fully + * to the remote host. This may not complete at this time, for several + * reasons: + * - we do EPTR and the server will not connect to our listen socket + * until we send more FTP commands + * - an SSL filter is in place and the server will not start the TLS + * handshake until we send more FTP commands + */ if(conn->cfilter[SECONDARYSOCKET]) { + bool is_eptr = Curl_conn_is_tcp_listen(data, SECONDARYSOCKET); result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected); - if(result || !Curl_conn_is_ip_connected(data, SECONDARYSOCKET)) { - if(result && (ftpc->count1 == 0)) { + if(result || (!connected && !is_eptr && + !Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) { + if(result && !is_eptr && (ftpc->count1 == 0)) { *completep = -1; /* go back to DOING please */ /* this is a EPSV connect failing, try PASV instead */ return ftp_epsv_disable(data, conn); } + *completep = (int)complete; return result; } } @@ -3764,16 +3595,14 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(ftpc->wait_data_conn) { bool serv_conned; - result = ReceivedServerConnect(data, &serv_conned); + result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &serv_conned); if(result) return result; /* Failed to accept data connection */ if(serv_conned) { /* It looks data connection is established */ - result = AcceptServerConnect(data); ftpc->wait_data_conn = FALSE; - if(!result) - result = InitiateTransfer(data); + result = InitiateTransfer(data); if(result) return result; @@ -3781,6 +3610,11 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) *completep = 1; /* this state is now complete when the server has connected back to us */ } + else { + result = ftp_check_ctrl_on_data_wait(data); + if(result) + return result; + } } else if(data->state.upload) { result = ftp_nb_type(data, conn, data->state.prefer_ascii, diff --git a/lib/getinfo.c b/lib/getinfo.c index 6e64733d486add..9144ad715df604 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -449,6 +449,9 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info, *param_offt = data->conn ? data->conn->connection_id : data->state.recent_conn_id; break; + case CURLINFO_EARLYDATA_SENT_T: + *param_offt = data->progress.earlydata_sent; + break; default: return CURLE_UNKNOWN_OPTION; } diff --git a/lib/hmac.c b/lib/hmac.c index 90f37f0bff31bd..088c9bdcec8920 100644 --- a/lib/hmac.c +++ b/lib/hmac.c @@ -49,8 +49,6 @@ static const unsigned char hmac_ipad = 0x36; static const unsigned char hmac_opad = 0x5C; - - struct HMAC_context * Curl_HMAC_init(const struct HMAC_params *hashparams, const unsigned char *key, @@ -62,42 +60,40 @@ Curl_HMAC_init(const struct HMAC_params *hashparams, unsigned char b; /* Create HMAC context. */ - i = sizeof(*ctxt) + 2 * hashparams->hmac_ctxtsize + - hashparams->hmac_resultlen; + i = sizeof(*ctxt) + 2 * hashparams->ctxtsize + hashparams->resultlen; ctxt = malloc(i); if(!ctxt) return ctxt; - ctxt->hmac_hash = hashparams; - ctxt->hmac_hashctxt1 = (void *) (ctxt + 1); - ctxt->hmac_hashctxt2 = (void *) ((char *) ctxt->hmac_hashctxt1 + - hashparams->hmac_ctxtsize); + ctxt->hash = hashparams; + ctxt->hashctxt1 = (void *) (ctxt + 1); + ctxt->hashctxt2 = (void *) ((char *) ctxt->hashctxt1 + hashparams->ctxtsize); /* If the key is too long, replace it by its hash digest. */ - if(keylen > hashparams->hmac_maxkeylen) { - (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, key, keylen); - hkey = (unsigned char *) ctxt->hmac_hashctxt2 + hashparams->hmac_ctxtsize; - (*hashparams->hmac_hfinal)(hkey, ctxt->hmac_hashctxt1); + if(keylen > hashparams->maxkeylen) { + hashparams->hinit(ctxt->hashctxt1); + hashparams->hupdate(ctxt->hashctxt1, key, keylen); + hkey = (unsigned char *) ctxt->hashctxt2 + hashparams->ctxtsize; + hashparams->hfinal(hkey, ctxt->hashctxt1); key = hkey; - keylen = hashparams->hmac_resultlen; + keylen = hashparams->resultlen; } /* Prime the two hash contexts with the modified key. */ - (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt1); - (*hashparams->hmac_hinit)(ctxt->hmac_hashctxt2); + hashparams->hinit(ctxt->hashctxt1); + hashparams->hinit(ctxt->hashctxt2); for(i = 0; i < keylen; i++) { b = (unsigned char)(*key ^ hmac_ipad); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &b, 1); + hashparams->hupdate(ctxt->hashctxt1, &b, 1); b = (unsigned char)(*key++ ^ hmac_opad); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &b, 1); + hashparams->hupdate(ctxt->hashctxt2, &b, 1); } - for(; i < hashparams->hmac_maxkeylen; i++) { - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt1, &hmac_ipad, 1); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, &hmac_opad, 1); + for(; i < hashparams->maxkeylen; i++) { + hashparams->hupdate(ctxt->hashctxt1, &hmac_ipad, 1); + hashparams->hupdate(ctxt->hashctxt2, &hmac_opad, 1); } /* Done, return pointer to HMAC context. */ @@ -105,31 +101,29 @@ Curl_HMAC_init(const struct HMAC_params *hashparams, } int Curl_HMAC_update(struct HMAC_context *ctxt, - const unsigned char *data, + const unsigned char *ptr, unsigned int len) { /* Update first hash calculation. */ - (*ctxt->hmac_hash->hmac_hupdate)(ctxt->hmac_hashctxt1, data, len); + ctxt->hash->hupdate(ctxt->hashctxt1, ptr, len); return 0; } -int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *result) +int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output) { - const struct HMAC_params *hashparams = ctxt->hmac_hash; + const struct HMAC_params *hashparams = ctxt->hash; - /* Do not get result if called with a null parameter: only release + /* Do not get output if called with a null parameter: only release storage. */ - if(!result) - result = (unsigned char *) ctxt->hmac_hashctxt2 + - ctxt->hmac_hash->hmac_ctxtsize; + if(!output) + output = (unsigned char *) ctxt->hashctxt2 + ctxt->hash->ctxtsize; - (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt1); - (*hashparams->hmac_hupdate)(ctxt->hmac_hashctxt2, - result, hashparams->hmac_resultlen); - (*hashparams->hmac_hfinal)(result, ctxt->hmac_hashctxt2); - free((char *) ctxt); + hashparams->hfinal(output, ctxt->hashctxt1); + hashparams->hupdate(ctxt->hashctxt2, output, hashparams->resultlen); + hashparams->hfinal(output, ctxt->hashctxt2); + free(ctxt); return 0; } @@ -144,15 +138,15 @@ int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *result) * hashparams [in] - The hash function (Curl_HMAC_MD5). * key [in] - The key to use. * keylen [in] - The length of the key. - * data [in] - The data to encrypt. - * datalen [in] - The length of the data. + * buf [in] - The data to encrypt. + * buflen [in] - The length of the data. * output [in/out] - The output buffer. * * Returns CURLE_OK on success. */ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, - const unsigned char *data, const size_t datalen, + const unsigned char *buf, const size_t buflen, unsigned char *output) { struct HMAC_context *ctxt = @@ -162,7 +156,7 @@ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, return CURLE_OUT_OF_MEMORY; /* Update the digest with the given challenge */ - Curl_HMAC_update(ctxt, data, curlx_uztoui(datalen)); + Curl_HMAC_update(ctxt, buf, curlx_uztoui(buflen)); /* Finalise the digest */ Curl_HMAC_final(ctxt, output); diff --git a/lib/hostip.c b/lib/hostip.c index fae5541921fce1..fe8cc5cb97fd48 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -630,7 +630,7 @@ bool Curl_ipv6works(struct Curl_easy *data) ipv6_works = 1; sclose(s); } - return (ipv6_works > 0) ? TRUE : FALSE; + return (ipv6_works > 0); } } #endif /* USE_IPV6 */ diff --git a/lib/hostip4.c b/lib/hostip4.c index 3bfea48d4fcc0c..58333fbc60763b 100644 --- a/lib/hostip4.c +++ b/lib/hostip4.c @@ -126,8 +126,10 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int res; #endif struct Curl_addrinfo *ai = NULL; +#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) struct hostent *h = NULL; struct hostent *buf = NULL; +#endif #if defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE) struct addrinfo hints; @@ -288,12 +290,14 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, #endif /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) || HAVE_GETHOSTBYNAME_R */ +#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) if(h) { ai = Curl_he2ai(h, port); if(buf) /* used a *_r() function */ free(buf); } +#endif return ai; } diff --git a/lib/hsts.c b/lib/hsts.c index a5c216f6de3805..5b0137263b08af 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -159,7 +159,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, do { while(*p && ISBLANK(*p)) p++; - if(strncasecompare("max-age=", p, 8)) { + if(strncasecompare("max-age", p, 7)) { bool quoted = FALSE; CURLofft offt; char *endp; @@ -167,9 +167,14 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, if(gotma) return CURLE_BAD_FUNCTION_ARGUMENT; - p += 8; + p += 7; while(*p && ISBLANK(*p)) p++; + if(*p++ != '=') + return CURLE_BAD_FUNCTION_ARGUMENT; + while(*p && ISBLANK(*p)) + p++; + if(*p == '\"') { p++; quoted = TRUE; @@ -249,24 +254,23 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, bool subdomain) { + struct stsentry *bestsub = NULL; if(h) { - char buffer[MAX_HSTS_HOSTLEN + 1]; time_t now = time(NULL); size_t hlen = strlen(hostname); struct Curl_llist_node *e; struct Curl_llist_node *n; + size_t blen = 0; if((hlen > MAX_HSTS_HOSTLEN) || !hlen) return NULL; - memcpy(buffer, hostname, hlen); if(hostname[hlen-1] == '.') /* remove the trailing dot */ --hlen; - buffer[hlen] = 0; - hostname = buffer; 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 */ @@ -274,20 +278,23 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, hsts_free(sts); continue; } - if(subdomain && sts->includeSubDomains) { - size_t ntail = strlen(sts->host); - if(ntail < hlen) { - size_t offs = hlen - ntail; - if((hostname[offs-1] == '.') && - strncasecompare(&hostname[offs], sts->host, ntail)) - return sts; + ntail = strlen(sts->host); + if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { + size_t offs = hlen - ntail; + if((hostname[offs-1] == '.') && + strncasecompare(&hostname[offs], sts->host, ntail) && + (ntail > blen)) { + /* save the tail match with the longest tail */ + bestsub = sts; + blen = ntail; } } - if(strcasecompare(hostname, sts->host)) + /* avoid strcasecompare because the host name is not null terminated */ + if((hlen == ntail) && strncasecompare(hostname, sts->host, hlen)) return sts; } } - return NULL; /* no match */ + return bestsub; } /* @@ -439,7 +446,7 @@ static CURLcode hsts_add(struct hsts *h, char *line) e = Curl_hsts(h, p, subdomain); if(!e) result = hsts_create(h, p, subdomain, expires); - else { + else if(strcasecompare(p, e->host)) { /* the same hostname, use the largest expire time */ if(expires > e->expires) e->expires = expires; diff --git a/lib/http.c b/lib/http.c index b0a397915bab22..a94edd61c23ec3 100644 --- a/lib/http.c +++ b/lib/http.c @@ -677,7 +677,7 @@ output_auth_headers(struct Curl_easy *data, auth, data->state.aptr.user ? data->state.aptr.user : ""); #endif - authstatus->multipass = (!authstatus->done) ? TRUE : FALSE; + authstatus->multipass = !authstatus->done; } else authstatus->multipass = FALSE; @@ -2248,8 +2248,9 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, addcookies = data->set.str[STRING_COOKIE]; if(data->cookies || addcookies) { - struct Cookie *co = NULL; /* no cookies from start */ + struct Curl_llist list; int count = 0; + int rc = 1; if(data->cookies && data->state.cookie_engine) { const char *host = data->state.aptr.cookiehost ? @@ -2258,17 +2259,19 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || strcasecompare("localhost", host) || !strcmp(host, "127.0.0.1") || - !strcmp(host, "::1") ? TRUE : FALSE; + !strcmp(host, "::1"); Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - co = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path, - secure_context); + rc = Curl_cookie_getlist(data, data->cookies, host, data->state.up.path, + secure_context, &list); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } - if(co) { - struct Cookie *store = co; + if(!rc) { + struct Curl_llist_node *n; size_t clen = 8; /* hold the size of the generated Cookie: header */ - /* now loop through all cookies that matched */ - while(co) { + + /* loop through all cookies that matched */ + for(n = Curl_llist_head(&list); n; n = Curl_node_next(n)) { + struct Cookie *co = Curl_node_elem(n); if(co->value) { size_t add; if(!count) { @@ -2290,9 +2293,8 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, clen += add + (count ? 2 : 0); count++; } - co = co->next; /* next cookie please */ } - Curl_cookie_freelist(store); + Curl_llist_destroy(&list, NULL); } if(addcookies && !result && !linecap) { if(!count) @@ -3039,8 +3041,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, char *persistentauth = Curl_copy_header_value(hd); if(!persistentauth) return CURLE_OUT_OF_MEMORY; - negdata->noauthpersist = checkprefix("false", persistentauth) ? - TRUE : FALSE; + negdata->noauthpersist = !!checkprefix("false", persistentauth); negdata->havenoauthpersist = TRUE; infof(data, "Negotiate: noauthpersist -> %d, header part: %s", negdata->noauthpersist, persistentauth); @@ -3081,7 +3082,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || strcasecompare("localhost", host) || !strcmp(host, "127.0.0.1") || - !strcmp(host, "::1") ? TRUE : FALSE; + !strcmp(host, "::1"); Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); @@ -4542,7 +4543,7 @@ static void http_exp100_send_anyway(struct Curl_easy *data) bool Curl_http_exp100_is_selected(struct Curl_easy *data) { struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100); - return r ? TRUE : FALSE; + return !!r; } #endif /* CURL_DISABLE_HTTP */ diff --git a/lib/http2.c b/lib/http2.c index 42abe1daae5a1c..dbe6f1aaae6866 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -433,6 +433,8 @@ static int h2_client_new(struct Curl_cfilter *cf, { struct cf_h2_ctx *ctx = cf->ctx; nghttp2_option *o; + nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free, + Curl_nghttp2_calloc, Curl_nghttp2_realloc}; int rc = nghttp2_option_new(&o); if(rc) @@ -445,7 +447,7 @@ static int h2_client_new(struct Curl_cfilter *cf, HTTP field value. */ nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1); #endif - rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o); + rc = nghttp2_session_client_new3(&ctx->h2, cbs, cf, o, &mem); nghttp2_option_del(o); return rc; } @@ -789,8 +791,11 @@ static ssize_t send_callback(nghttp2_session *h2, (void)flags; DEBUGASSERT(data); - nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, - nw_out_writer, cf, &result); + if(!cf->connected) + nwritten = Curl_bufq_write(&ctx->outbufq, buf, blen, &result); + else + nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, + nw_out_writer, cf, &result); if(nwritten < 0) { if(result == CURLE_AGAIN) { ctx->nw_out_blocked = 1; @@ -1116,9 +1121,6 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; } } - if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - drain_stream(cf, data, stream); - } break; case NGHTTP2_HEADERS: if(stream->bodystarted) { @@ -1134,10 +1136,10 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, return CURLE_RECV_ERROR; /* Only final status code signals the end of header */ - if(stream->status_code / 100 != 1) { + if(stream->status_code / 100 != 1) stream->bodystarted = TRUE; + else stream->status_code = -1; - } h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed); @@ -1184,6 +1186,22 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, default: break; } + + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + if(!stream->closed && !stream->body_eos && + ((stream->status_code >= 400) || (stream->status_code < 200))) { + /* The server did not give us a positive response and we are not + * done uploading the request body. We need to stop doing that and + * also inform the server that we aborted our side. */ + CURL_TRC_CF(data, cf, "[%d] EOS frame with unfinished upload and " + "HTTP status %d, abort upload by RST", + stream_id, stream->status_code); + nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE, + stream->id, NGHTTP2_STREAM_CLOSED); + stream->closed = TRUE; + } + drain_stream(cf, data, stream); + } return CURLE_OK; } @@ -1898,6 +1916,11 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, nghttp2_strerror(rv), rv); return CURLE_SEND_ERROR; } + /* Defer flushing during the connect phase so that the SETTINGS and + * other initial frames are sent together with the first request. + * Unless we are 'connect_only' where the request will never come. */ + if(!cf->connected && !cf->conn->connect_only) + return CURLE_OK; return nw_out_flush(cf, data); } @@ -2439,6 +2462,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, struct cf_h2_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; struct cf_call_data save; + bool first_time = FALSE; if(cf->connected) { *done = TRUE; @@ -2460,11 +2484,14 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf, result = cf_h2_ctx_open(cf, data); if(result) goto out; + first_time = TRUE; } - result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE); - if(result) - goto out; + if(!first_time) { + result = h2_progress_ingress(cf, data, H2_CHUNK_SIZE); + if(result) + goto out; + } /* Send out our SETTINGS and ACKs and such. If that blocks, we * have it buffered and can count this filter as being connected */ @@ -2785,8 +2812,8 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf, return result; } -static bool Curl_cf_is_http2(struct Curl_cfilter *cf, - const struct Curl_easy *data) +static bool cf_is_http2(struct Curl_cfilter *cf, + const struct Curl_easy *data) { (void)data; for(; cf; cf = cf->next) { @@ -2802,7 +2829,7 @@ bool Curl_conn_is_http2(const struct Curl_easy *data, const struct connectdata *conn, int sockindex) { - return conn ? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE; + return conn ? cf_is_http2(conn->cfilter[sockindex], data) : FALSE; } bool Curl_http2_may_switch(struct Curl_easy *data, @@ -2854,7 +2881,7 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data) struct Curl_cfilter *cf_h2; CURLcode result; - DEBUGASSERT(!Curl_cf_is_http2(cf, data)); + DEBUGASSERT(!cf_is_http2(cf, data)); result = http2_cfilter_insert_after(cf, data, FALSE); if(result) @@ -2935,6 +2962,30 @@ bool Curl_h2_http_1_1_error(struct Curl_easy *data) return FALSE; } +void *Curl_nghttp2_malloc(size_t size, void *user_data) +{ + (void)user_data; + return Curl_cmalloc(size); +} + +void Curl_nghttp2_free(void *ptr, void *user_data) +{ + (void)user_data; + Curl_cfree(ptr); +} + +void *Curl_nghttp2_calloc(size_t nmemb, size_t size, void *user_data) +{ + (void)user_data; + return Curl_ccalloc(nmemb, size); +} + +void *Curl_nghttp2_realloc(void *ptr, size_t size, void *user_data) +{ + (void)user_data; + return Curl_crealloc(ptr, size); +} + #else /* !USE_NGHTTP2 */ /* Satisfy external references even if http2 is not compiled in. */ diff --git a/lib/http2.h b/lib/http2.h index 80e183480a70dc..dbb1784661e744 100644 --- a/lib/http2.h +++ b/lib/http2.h @@ -60,6 +60,11 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, struct connectdata *conn, int sockindex, const char *ptr, size_t nread); +void *Curl_nghttp2_malloc(size_t size, void *user_data); +void Curl_nghttp2_free(void *ptr, void *user_data); +void *Curl_nghttp2_calloc(size_t nmemb, size_t size, void *user_data); +void *Curl_nghttp2_realloc(void *ptr, size_t size, void *user_data); + extern struct Curl_cftype Curl_cft_nghttp2; #else /* USE_NGHTTP2 */ diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index a97ab808f0ce8a..5d4848fed2b4dc 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -47,7 +47,7 @@ #define HMAC_SHA256(k, kl, d, dl, o) \ do { \ - result = Curl_hmacit(Curl_HMAC_SHA256, \ + result = Curl_hmacit(&Curl_HMAC_SHA256, \ (unsigned char *)k, \ kl, \ (unsigned char *)d, \ @@ -122,10 +122,6 @@ static void trim_headers(struct curl_slist *head) #define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date")) -#define MAX_HOST_LEN 255 -/* FQDN + host: */ -#define FULL_HOST_LEN (MAX_HOST_LEN + sizeof("host:")) - /* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */ #define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1) @@ -190,31 +186,22 @@ static CURLcode make_headers(struct Curl_easy *data, "x-%s-date:%s", provider1, timestamp); if(!Curl_checkheaders(data, STRCONST("Host"))) { - char full_host[FULL_HOST_LEN + 1]; + char *fullhost; if(data->state.aptr.host) { - size_t pos; - - if(strlen(data->state.aptr.host) > FULL_HOST_LEN) { - ret = CURLE_URL_MALFORMAT; - goto fail; - } - strcpy(full_host, data->state.aptr.host); /* remove /r/n as the separator for canonical request must be '\n' */ - pos = strcspn(full_host, "\n\r"); - full_host[pos] = 0; - } - else { - if(strlen(hostname) > MAX_HOST_LEN) { - ret = CURLE_URL_MALFORMAT; - goto fail; - } - msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname); + size_t pos = strcspn(data->state.aptr.host, "\n\r"); + fullhost = Curl_memdup0(data->state.aptr.host, pos); } + else + fullhost = aprintf("host:%s", hostname); - head = curl_slist_append(NULL, full_host); - if(!head) + if(fullhost) + head = Curl_slist_append_nodup(NULL, fullhost); + if(!head) { + free(fullhost); goto fail; + } } diff --git a/lib/http_digest.c b/lib/http_digest.c index 2db3125a8e6656..a3ba17a51fa1b2 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -121,9 +121,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data, passwdp = ""; #if defined(USE_WINDOWS_SSPI) - have_chlg = digest->input_token ? TRUE : FALSE; + have_chlg = !!digest->input_token; #else - have_chlg = digest->nonce ? TRUE : FALSE; + have_chlg = !!digest->nonce; #endif if(!have_chlg) { diff --git a/lib/imap.c b/lib/imap.c index 77e527aaf195ca..e424cdb056a40f 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -1399,7 +1399,7 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done) } result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE); - *done = (imapc->state == IMAP_STOP) ? TRUE : FALSE; + *done = (imapc->state == IMAP_STOP); return result; } diff --git a/lib/krb5.c b/lib/krb5.c index d586498640f505..e310a1b57a8ff0 100644 --- a/lib/krb5.c +++ b/lib/krb5.c @@ -202,7 +202,8 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) data->set.str[STRING_SERVICE_NAME] : "ftp"; const char *srv_host = "host"; - gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp; + gss_buffer_desc input_buffer, output_buffer, *gssresp; + gss_buffer_desc _gssresp = GSS_C_EMPTY_BUFFER; OM_uint32 maj, min; gss_name_t gssname; gss_ctx_id_t *context = app_data; @@ -363,7 +364,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) free(_gssresp.value); if(ret == AUTH_OK || service == srv_host) - return ret; + break; service = srv_host; } @@ -372,13 +373,13 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) static void krb5_end(void *app_data) { - OM_uint32 min; - gss_ctx_id_t *context = app_data; - if(*context != GSS_C_NO_CONTEXT) { - OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER); - (void)maj; - DEBUGASSERT(maj == GSS_S_COMPLETE); - } + OM_uint32 min; + gss_ctx_id_t *context = app_data; + if(*context != GSS_C_NO_CONTEXT) { + OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER); + (void)maj; + DEBUGASSERT(maj == GSS_S_COMPLETE); + } } static const struct Curl_sec_client_mech Curl_krb5_client_mech = { @@ -612,10 +613,10 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex, return total_read; } -/* Send |length| bytes from |from| to the |fd| socket taking care of encoding - and negotiating with the server. |from| can be NULL. */ +/* Send |length| bytes from |from| to the |sockindex| socket taking care of + encoding and negotiating with the server. |from| can be NULL. */ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t fd, const char *from, int length) + int sockindex, const char *from, int length) { int bytes, htonl_bytes; /* 32-bit integers for htonl */ char *buffer = NULL; @@ -623,7 +624,7 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, size_t cmd_size = 0; CURLcode error; enum protection_level prot_level = conn->data_prot; - bool iscmd = (prot_level == PROT_CMD) ? TRUE : FALSE; + bool iscmd = (prot_level == PROT_CMD); DEBUGASSERT(prot_level > PROT_NONE && prot_level < PROT_LAST); @@ -649,12 +650,12 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, static const char *enc = "ENC "; static const char *mic = "MIC "; if(prot_level == PROT_PRIVATE) - socket_write(data, fd, enc, 4); + socket_write(data, sockindex, enc, 4); else - socket_write(data, fd, mic, 4); + socket_write(data, sockindex, mic, 4); - socket_write(data, fd, cmd_buffer, cmd_size); - socket_write(data, fd, "\r\n", 2); + socket_write(data, sockindex, cmd_buffer, cmd_size); + socket_write(data, sockindex, "\r\n", 2); infof(data, "Send: %s%s", prot_level == PROT_PRIVATE ? enc : mic, cmd_buffer); free(cmd_buffer); @@ -662,14 +663,14 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, } else { htonl_bytes = (int)htonl((OM_uint32)bytes); - socket_write(data, fd, &htonl_bytes, sizeof(htonl_bytes)); - socket_write(data, fd, buffer, curlx_sitouz(bytes)); + socket_write(data, sockindex, &htonl_bytes, sizeof(htonl_bytes)); + socket_write(data, sockindex, buffer, curlx_sitouz(bytes)); } free(buffer); } static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t fd, const char *buffer, size_t length) + int sockindex, const char *buffer, size_t length) { ssize_t tx = 0, len = conn->buffer_size; @@ -679,7 +680,7 @@ static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn, if(length < (size_t)len) len = length; - do_sec_send(data, conn, fd, buffer, curlx_sztosi(len)); + do_sec_send(data, conn, sockindex, buffer, curlx_sztosi(len)); length -= len; buffer += len; tx += len; @@ -693,10 +694,9 @@ static ssize_t sec_send(struct Curl_easy *data, int sockindex, CURLcode *err) { struct connectdata *conn = data->conn; - curl_socket_t fd = conn->sock[sockindex]; (void)eos; /* unused */ *err = CURLE_OK; - return sec_write(data, conn, fd, buffer, len); + return sec_write(data, conn, sockindex, buffer, len); } int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, diff --git a/lib/ldap.c b/lib/ldap.c index 01429ba79e8bd6..2cbdb9c2142554 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -825,8 +825,8 @@ static bool split_str(char *str, char ***out, size_t *count) if(!res) return FALSE; - for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items; - s = strtok_r(NULL, ",", &lasts), i++) + for(i = 0, s = Curl_strtok_r(str, ",", &lasts); s && i < items; + s = Curl_strtok_r(NULL, ",", &lasts), i++) res[i] = s; *out = res; diff --git a/lib/md5.c b/lib/md5.c index 7b51429b484e45..73e04e37c10c03 100644 --- a/lib/md5.c +++ b/lib/md5.c @@ -88,20 +88,20 @@ typedef struct md5_ctx my_md5_ctx; -static CURLcode my_md5_init(my_md5_ctx *ctx) +static CURLcode my_md5_init(void *ctx) { md5_init(ctx); return CURLE_OK; } -static void my_md5_update(my_md5_ctx *ctx, +static void my_md5_update(void *ctx, const unsigned char *input, unsigned int inputLen) { md5_update(ctx, inputLen, input); } -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +static void my_md5_final(unsigned char *digest, void *ctx) { md5_digest(ctx, 16, digest); } @@ -110,7 +110,7 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) typedef MD5_CTX my_md5_ctx; -static CURLcode my_md5_init(my_md5_ctx *ctx) +static CURLcode my_md5_init(void *ctx) { if(!MD5_Init(ctx)) return CURLE_OUT_OF_MEMORY; @@ -118,14 +118,14 @@ static CURLcode my_md5_init(my_md5_ctx *ctx) return CURLE_OK; } -static void my_md5_update(my_md5_ctx *ctx, +static void my_md5_update(void *ctx, const unsigned char *input, unsigned int len) { (void)MD5_Update(ctx, input, len); } -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +static void my_md5_final(unsigned char *digest, void *ctx) { (void)MD5_Final(digest, ctx); } @@ -134,7 +134,7 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) typedef mbedtls_md5_context my_md5_ctx; -static CURLcode my_md5_init(my_md5_ctx *ctx) +static CURLcode my_md5_init(void *ctx) { #if (MBEDTLS_VERSION_NUMBER >= 0x03000000) if(mbedtls_md5_starts(ctx)) @@ -148,7 +148,7 @@ static CURLcode my_md5_init(my_md5_ctx *ctx) return CURLE_OK; } -static void my_md5_update(my_md5_ctx *ctx, +static void my_md5_update(void *ctx, const unsigned char *data, unsigned int length) { @@ -159,7 +159,7 @@ static void my_md5_update(my_md5_ctx *ctx, #endif } -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +static void my_md5_final(unsigned char *digest, void *ctx) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_md5_finish(ctx, digest); @@ -178,7 +178,7 @@ static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) reliable than defining COMMON_DIGEST_FOR_OPENSSL on older cats. */ # define my_md5_ctx CC_MD5_CTX -static CURLcode my_md5_init(my_md5_ctx *ctx) +static CURLcode my_md5_init(void *ctx) { if(!CC_MD5_Init(ctx)) return CURLE_OUT_OF_MEMORY; @@ -186,14 +186,14 @@ static CURLcode my_md5_init(my_md5_ctx *ctx) return CURLE_OK; } -static void my_md5_update(my_md5_ctx *ctx, +static void my_md5_update(void *ctx, const unsigned char *input, unsigned int inputLen) { CC_MD5_Update(ctx, input, inputLen); } -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +static void my_md5_final(unsigned char *digest, void *ctx) { CC_MD5_Final(digest, ctx); } @@ -206,8 +206,9 @@ struct md5_ctx { }; typedef struct md5_ctx my_md5_ctx; -static CURLcode my_md5_init(my_md5_ctx *ctx) +static CURLcode my_md5_init(void *in) { + my_md5_ctx *ctx = (my_md5_ctx *)in; if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) return CURLE_OUT_OF_MEMORY; @@ -221,15 +222,17 @@ static CURLcode my_md5_init(my_md5_ctx *ctx) return CURLE_OK; } -static void my_md5_update(my_md5_ctx *ctx, +static void my_md5_update(void *in, const unsigned char *input, unsigned int inputLen) { + my_md5_ctx *ctx = in; CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0); } -static void my_md5_final(unsigned char *digest, my_md5_ctx *ctx) +static void my_md5_final(unsigned char *digest, void *in) { + my_md5_ctx *ctx = (my_md5_ctx *)in; unsigned long length = 0; CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); if(length == 16) @@ -292,10 +295,10 @@ struct md5_ctx { }; typedef struct md5_ctx my_md5_ctx; -static CURLcode my_md5_init(my_md5_ctx *ctx); -static void my_md5_update(my_md5_ctx *ctx, const void *data, - unsigned long size); -static void my_md5_final(unsigned char *result, my_md5_ctx *ctx); +static CURLcode my_md5_init(void *ctx); +static void my_md5_update(void *ctx, const unsigned char *data, + unsigned int size); +static void my_md5_final(unsigned char *result, void *ctx); /* * The basic MD5 functions. @@ -455,8 +458,9 @@ static const void *my_md5_body(my_md5_ctx *ctx, return ptr; } -static CURLcode my_md5_init(my_md5_ctx *ctx) +static CURLcode my_md5_init(void *in) { + my_md5_ctx *ctx = (my_md5_ctx *)in; ctx->a = 0x67452301; ctx->b = 0xefcdab89; ctx->c = 0x98badcfe; @@ -468,11 +472,12 @@ static CURLcode my_md5_init(my_md5_ctx *ctx) return CURLE_OK; } -static void my_md5_update(my_md5_ctx *ctx, const void *data, - unsigned long size) +static void my_md5_update(void *in, const unsigned char *data, + unsigned int size) { MD5_u32plus saved_lo; - unsigned long used; + unsigned int used; + my_md5_ctx *ctx = (my_md5_ctx *)in; saved_lo = ctx->lo; ctx->lo = (saved_lo + size) & 0x1fffffff; @@ -483,7 +488,7 @@ static void my_md5_update(my_md5_ctx *ctx, const void *data, used = saved_lo & 0x3f; if(used) { - unsigned long available = 64 - used; + unsigned int available = 64 - used; if(size < available) { memcpy(&ctx->buffer[used], data, size); @@ -504,9 +509,10 @@ static void my_md5_update(my_md5_ctx *ctx, const void *data, memcpy(ctx->buffer, data, size); } -static void my_md5_final(unsigned char *result, my_md5_ctx *ctx) +static void my_md5_final(unsigned char *result, void *in) { - unsigned long used, available; + unsigned int used, available; + my_md5_ctx *ctx = (my_md5_ctx *)in; used = ctx->lo & 0x3f; @@ -557,36 +563,21 @@ static void my_md5_final(unsigned char *result, my_md5_ctx *ctx) #endif /* CRYPTO LIBS */ -const struct HMAC_params Curl_HMAC_MD5[] = { - { - /* Hash initialization function. */ - CURLX_FUNCTION_CAST(HMAC_hinit_func, my_md5_init), - /* Hash update function. */ - CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_md5_update), - /* Hash computation end function. */ - CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_md5_final), - /* Size of hash context structure. */ - sizeof(my_md5_ctx), - /* Maximum key length. */ - 64, - /* Result size. */ - 16 - } +const struct HMAC_params Curl_HMAC_MD5 = { + my_md5_init, /* Hash initialization function. */ + my_md5_update, /* Hash update function. */ + my_md5_final, /* Hash computation end function. */ + sizeof(my_md5_ctx), /* Size of hash context structure. */ + 64, /* Maximum key length. */ + 16 /* Result size. */ }; -const struct MD5_params Curl_DIGEST_MD5[] = { - { - /* Digest initialization function */ - CURLX_FUNCTION_CAST(Curl_MD5_init_func, my_md5_init), - /* Digest update function */ - CURLX_FUNCTION_CAST(Curl_MD5_update_func, my_md5_update), - /* Digest computation end function */ - CURLX_FUNCTION_CAST(Curl_MD5_final_func, my_md5_final), - /* Size of digest context struct */ - sizeof(my_md5_ctx), - /* Result size */ - 16 - } +const struct MD5_params Curl_DIGEST_MD5 = { + my_md5_init, /* Digest initialization function */ + my_md5_update, /* Digest update function */ + my_md5_final, /* Digest computation end function */ + sizeof(my_md5_ctx), /* Size of digest context struct */ + 16 /* Result size */ }; /* diff --git a/lib/memdebug.c b/lib/memdebug.c index bc83d3eea30b99..02612c2a23f179 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -49,9 +49,9 @@ struct memdebug { }; /* - * Note that these debug functions are very simple and they are meant to - * remain so. For advanced analysis, record a log file and write perl scripts - * to analyze them! + * Note that these debug functions are simple and they are meant to remain so. + * For advanced analysis, record a log file and write perl scripts to analyze + * them! * * Do not use these with multithreaded test programs! */ diff --git a/lib/mime.c b/lib/mime.c index 968fc3daee1e3f..70286069ce071c 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -26,6 +26,8 @@ #include +struct Curl_easy; + #include "mime.h" #include "warnless.h" #include "urldata.h" @@ -1279,7 +1281,7 @@ CURLcode Curl_mime_duppart(struct Curl_easy *data, */ /* Create a mime handle. */ -curl_mime *curl_mime_init(struct Curl_easy *easy) +curl_mime *curl_mime_init(void *easy) { curl_mime *mime; diff --git a/lib/mprintf.c b/lib/mprintf.c index 6a41d36f62c995..6576f3ebf9a395 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -455,15 +455,30 @@ static int parsefmt(const char *format, flags |= FLAGS_UNSIGNED; break; case 'o': - type = FORMAT_INT; - flags |= FLAGS_OCTAL; + if(flags & FLAGS_LONGLONG) + type = FORMAT_LONGLONGU; + else if(flags & FLAGS_LONG) + type = FORMAT_LONGU; + else + type = FORMAT_INTU; + flags |= FLAGS_OCTAL|FLAGS_UNSIGNED; break; case 'x': - type = FORMAT_INTU; + if(flags & FLAGS_LONGLONG) + type = FORMAT_LONGLONGU; + else if(flags & FLAGS_LONG) + type = FORMAT_LONGU; + else + type = FORMAT_INTU; flags |= FLAGS_HEX|FLAGS_UNSIGNED; break; case 'X': - type = FORMAT_INTU; + if(flags & FLAGS_LONGLONG) + type = FORMAT_LONGLONGU; + else if(flags & FLAGS_LONG) + type = FORMAT_LONGU; + else + type = FORMAT_INTU; flags |= FLAGS_HEX|FLAGS_UPPER|FLAGS_UNSIGNED; break; case 'c': diff --git a/lib/multi.c b/lib/multi.c index 65ba23de2a15f5..49fddc47203878 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -50,6 +50,7 @@ #include "http2.h" #include "socketpair.h" #include "socks.h" +#include "urlapi-int.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -79,7 +80,7 @@ * 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: \ + (((x) && (x)->magic == CURL_MULTI_HANDLE)? TRUE: \ (DEBUGASSERT(!(x)), FALSE)) #else #define GOOD_MULTI_HANDLE(x) \ @@ -98,8 +99,8 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, long *timeout_ms); static void process_pending_handles(struct Curl_multi *multi); static void multi_xfer_bufs_free(struct Curl_multi *multi); -static void Curl_expire_ex(struct Curl_easy *data, const struct curltime *nowp, - timediff_t milli, expire_id id); +static void expire_ex(struct Curl_easy *data, const struct curltime *nowp, + timediff_t milli, expire_id id); #if defined( DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) static const char * const multi_statename[]={ @@ -450,7 +451,7 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */ return NULL; } -struct Curl_multi *curl_multi_init(void) +CURLM *curl_multi_init(void) { return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE, CURL_CONNECTION_HASH_SIZE, @@ -471,10 +472,11 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) #define multi_warn_debug(x,y) Curl_nop_stmt #endif -CURLMcode curl_multi_add_handle(struct Curl_multi *multi, - struct Curl_easy *data) +CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) { CURLMcode rc; + struct Curl_multi *multi = m; + struct Curl_easy *data = d; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -756,6 +758,8 @@ static CURLcode multi_done(struct Curl_easy *data, mdctx.premature = premature; Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); + /* flush the netrc cache */ + Curl_netrc_cleanup(&data->state.netrc); return result; } @@ -769,10 +773,10 @@ static void close_connect_only(struct connectdata *conn, connclose(conn, "Removing connect-only easy handle"); } -CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, - struct Curl_easy *data) +CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) { - struct Curl_easy *easy = data; + struct Curl_multi *multi = m; + struct Curl_easy *data = d; bool premature; struct Curl_llist_node *e; CURLMcode rc; @@ -797,7 +801,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - premature = (data->mstate < MSTATE_COMPLETED) ? TRUE : FALSE; + premature = (data->mstate < MSTATE_COMPLETED); /* If the 'state' is not INIT or COMPLETED, we might need to do something nice to put the easy_handle in a good known state when this returns. */ @@ -847,7 +851,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, /* This ignores the return code even in case of problems because there is nothing more to do about that, here */ - (void)singlesocket(multi, easy); /* to let the application know what sockets + (void)singlesocket(multi, data); /* to let the application know what sockets that vanish with this handle */ /* Remove the association between the connection and the handle */ @@ -887,7 +891,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi, for(e = Curl_llist_head(&multi->msglist); e; e = Curl_node_next(e)) { struct Curl_message *msg = Curl_node_elem(e); - if(msg->extmsg.easy_handle == easy) { + if(msg->extmsg.easy_handle == data) { Curl_node_remove(e); /* there can only be one from this specific handle */ break; @@ -1138,7 +1142,7 @@ static void multi_getsock(struct Curl_easy *data, } } -CURLMcode curl_multi_fdset(struct Curl_multi *multi, +CURLMcode curl_multi_fdset(CURLM *m, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd) { @@ -1147,6 +1151,7 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, and then we must make sure that is done. */ int this_max_fd = -1; struct Curl_llist_node *e; + struct Curl_multi *multi = m; (void)exc_fd_set; /* not used */ if(!GOOD_MULTI_HANDLE(multi)) @@ -1179,7 +1184,7 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi, return CURLM_OK; } -CURLMcode curl_multi_waitfds(struct Curl_multi *multi, +CURLMcode curl_multi_waitfds(CURLM *m, struct curl_waitfd *ufds, unsigned int size, unsigned int *fd_count) @@ -1187,6 +1192,7 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi, struct curl_waitfds cwfds; CURLMcode result = CURLM_OK; struct Curl_llist_node *e; + struct Curl_multi *multi = m; if(!ufds) return CURLM_BAD_FUNCTION_ARGUMENT; @@ -1484,7 +1490,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, return result; } -CURLMcode curl_multi_wait(struct Curl_multi *multi, +CURLMcode curl_multi_wait(CURLM *multi, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -1494,7 +1500,7 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi, FALSE); } -CURLMcode curl_multi_poll(struct Curl_multi *multi, +CURLMcode curl_multi_poll(CURLM *multi, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -1504,11 +1510,12 @@ CURLMcode curl_multi_poll(struct Curl_multi *multi, TRUE); } -CURLMcode curl_multi_wakeup(struct Curl_multi *multi) +CURLMcode curl_multi_wakeup(CURLM *m) { /* this function is usually called from another thread, it has to be careful only to access parts of the Curl_multi struct that are constant */ + struct Curl_multi *multi = m; #if defined(ENABLE_WAKEUP) && !defined(USE_WINSOCK) #ifdef USE_EVENTFD @@ -1534,6 +1541,9 @@ CURLMcode curl_multi_wakeup(struct Curl_multi *multi) if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) { #ifdef USE_EVENTFD buf = &val; + /* eventfd has a stringent rule of requiring the 8-byte buffer when + calling write(2) on it, which makes the sizeof(buf) below fine since + this is only used on 64-bit systems and then the pointer is 64-bit */ #else buf[0] = 1; #endif @@ -1817,19 +1827,781 @@ static void multi_posttransfer(struct Curl_easy *data) #endif } +/* + * multi_follow() handles the URL redirect magic. Pass in the 'newurl' string + * as given by the remote server and set up the new URL to request. + * + * This function DOES NOT FREE the given url. + */ +static CURLcode multi_follow(struct Curl_easy *data, + char *newurl, /* the Location: string */ + followtype type) /* see transfer.h */ +{ +#ifdef CURL_DISABLE_HTTP + (void)data; + (void)newurl; + (void)type; + /* Location: following will not happen when HTTP is disabled */ + return CURLE_TOO_MANY_REDIRECTS; +#else + + /* Location: redirect */ + bool disallowport = FALSE; + bool reachedmax = FALSE; + CURLUcode uc; + + DEBUGASSERT(type != FOLLOW_NONE); + + if(type != FOLLOW_FAKE) + data->state.requests++; /* count all real follows */ + if(type == FOLLOW_REDIR) { + if((data->set.maxredirs != -1) && + (data->state.followlocation >= data->set.maxredirs)) { + reachedmax = TRUE; + type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected + to URL */ + } + else { + data->state.followlocation++; /* count redirect-followings, including + auth reloads */ + + if(data->set.http_auto_referer) { + CURLU *u; + char *referer = NULL; + + /* We are asked to automatically set the previous URL as the referer + when we get the next URL. We pick the ->url field, which may or may + not be 100% correct */ + + if(data->state.referer_alloc) { + Curl_safefree(data->state.referer); + data->state.referer_alloc = FALSE; + } + + /* Make a copy of the URL without credentials and fragment */ + u = curl_url(); + if(!u) + return CURLE_OUT_OF_MEMORY; + + uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_USER, NULL, 0); + if(!uc) + uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0); + if(!uc) + uc = curl_url_get(u, CURLUPART_URL, &referer, 0); + + curl_url_cleanup(u); + + if(uc || !referer) + return CURLE_OUT_OF_MEMORY; + + data->state.referer = referer; + data->state.referer_alloc = TRUE; /* yes, free this later */ + } + } + } + + if((type != FOLLOW_RETRY) && + (data->req.httpcode != 401) && (data->req.httpcode != 407) && + Curl_is_absolute_url(newurl, NULL, 0, FALSE)) { + /* If this is not redirect due to a 401 or 407 response and an absolute + URL: do not allow a custom port number */ + disallowport = TRUE; + } + + DEBUGASSERT(data->state.uh); + uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int) + ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : + ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) | + CURLU_ALLOW_SPACE | + (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); + if(uc) { + if(type != FOLLOW_FAKE) { + failf(data, "The redirect target URL could not be parsed: %s", + curl_url_strerror(uc)); + return Curl_uc_to_curlcode(uc); + } + + /* the URL could not be parsed for some reason, but since this is FAKE + mode, just duplicate the field as-is */ + newurl = strdup(newurl); + if(!newurl) + return CURLE_OUT_OF_MEMORY; + } + else { + uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + + /* Clear auth if this redirects to a different port number or protocol, + unless permitted */ + if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { + char *portnum; + int port; + bool clear = FALSE; + + if(data->set.use_port && data->state.allow_port) + /* a custom port is used */ + port = (int)data->set.use_port; + else { + uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, + CURLU_DEFAULT_PORT); + if(uc) { + free(newurl); + return Curl_uc_to_curlcode(uc); + } + port = atoi(portnum); + free(portnum); + } + if(port != data->info.conn_remote_port) { + infof(data, "Clear auth, redirects to port from %u to %u", + data->info.conn_remote_port, port); + clear = TRUE; + } + else { + char *scheme; + const struct Curl_handler *p; + uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); + if(uc) { + free(newurl); + return Curl_uc_to_curlcode(uc); + } + + p = Curl_get_scheme_handler(scheme); + if(p && (p->protocol != data->info.conn_protocol)) { + infof(data, "Clear auth, redirects scheme from %s to %s", + data->info.conn_scheme, scheme); + clear = TRUE; + } + free(scheme); + } + if(clear) { + Curl_safefree(data->state.aptr.user); + Curl_safefree(data->state.aptr.passwd); + } + } + } + + if(type == FOLLOW_FAKE) { + /* we are only figuring out the new URL if we would have followed locations + but now we are done so we can get out! */ + data->info.wouldredirect = newurl; + + if(reachedmax) { + failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs); + return CURLE_TOO_MANY_REDIRECTS; + } + return CURLE_OK; + } + + if(disallowport) + data->state.allow_port = FALSE; + + if(data->state.url_alloc) + Curl_safefree(data->state.url); + + data->state.url = newurl; + data->state.url_alloc = TRUE; + Curl_req_soft_reset(&data->req, data); + infof(data, "Issue another request to this URL: '%s'", data->state.url); + + /* + * We get here when the HTTP code is 300-399 (and 401). We need to perform + * differently based on exactly what return code there was. + * + * News from 7.10.6: we can also get here on a 401 or 407, in case we act on + * an HTTP (proxy-) authentication scheme other than Basic. + */ + switch(data->info.httpcode) { + /* 401 - Act on a WWW-Authenticate, we keep on moving and do the + Authorization: XXXX header in the HTTP request code snippet */ + /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the + Proxy-Authorization: XXXX header in the HTTP request code snippet */ + /* 300 - Multiple Choices */ + /* 306 - Not used */ + /* 307 - Temporary Redirect */ + default: /* for all above (and the unknown ones) */ + /* Some codes are explicitly mentioned since I have checked RFC2616 and + * they seem to be OK to POST to. + */ + break; + case 301: /* Moved Permanently */ + /* (quote from RFC7231, section 6.4.2) + * + * Note: For historical reasons, a user agent MAY change the request + * method from POST to GET for the subsequent request. If this + * behavior is undesired, the 307 (Temporary Redirect) status code + * can be used instead. + * + * ---- + * + * Many webservers expect this, so these servers often answers to a POST + * request with an error page. To be sure that libcurl gets the page that + * most user agents would get, libcurl has to force GET. + * + * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and + * can be overridden with CURLOPT_POSTREDIR. + */ + if((data->state.httpreq == HTTPREQ_POST + || data->state.httpreq == HTTPREQ_POST_FORM + || data->state.httpreq == HTTPREQ_POST_MIME) + && !(data->set.keep_post & CURL_REDIR_POST_301)) { + infof(data, "Switch from POST to GET"); + data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); + } + break; + case 302: /* Found */ + /* (quote from RFC7231, section 6.4.3) + * + * Note: For historical reasons, a user agent MAY change the request + * method from POST to GET for the subsequent request. If this + * behavior is undesired, the 307 (Temporary Redirect) status code + * can be used instead. + * + * ---- + * + * Many webservers expect this, so these servers often answers to a POST + * request with an error page. To be sure that libcurl gets the page that + * most user agents would get, libcurl has to force GET. + * + * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and + * can be overridden with CURLOPT_POSTREDIR. + */ + if((data->state.httpreq == HTTPREQ_POST + || data->state.httpreq == HTTPREQ_POST_FORM + || data->state.httpreq == HTTPREQ_POST_MIME) + && !(data->set.keep_post & CURL_REDIR_POST_302)) { + infof(data, "Switch from POST to GET"); + data->state.httpreq = HTTPREQ_GET; + Curl_creader_set_rewind(data, FALSE); + } + break; + + case 303: /* See Other */ + /* 'See Other' location is not the resource but a substitute for the + * resource. In this case we switch the method to GET/HEAD, unless the + * method is POST and the user specified to keep it as POST. + * https://github.com/curl/curl/issues/5237#issuecomment-614641049 + */ + if(data->state.httpreq != HTTPREQ_GET && + ((data->state.httpreq != HTTPREQ_POST && + data->state.httpreq != HTTPREQ_POST_FORM && + data->state.httpreq != HTTPREQ_POST_MIME) || + !(data->set.keep_post & CURL_REDIR_POST_303))) { + data->state.httpreq = HTTPREQ_GET; + infof(data, "Switch to %s", + data->req.no_body ? "HEAD" : "GET"); + } + break; + case 304: /* Not Modified */ + /* 304 means we did a conditional request and it was "Not modified". + * We should not get any Location: header in this response! + */ + break; + case 305: /* Use Proxy */ + /* (quote from RFC2616, section 10.3.6): + * "The requested resource MUST be accessed through the proxy given + * by the Location field. The Location field gives the URI of the + * proxy. The recipient is expected to repeat this single request + * via the proxy. 305 responses MUST only be generated by origin + * servers." + */ + break; + } + Curl_pgrsTime(data, TIMER_REDIRECT); + Curl_pgrsResetTransferSizes(data); + + return CURLE_OK; +#endif /* CURL_DISABLE_HTTP */ +} + +static CURLMcode state_performing(struct Curl_easy *data, + struct curltime *nowp, + bool *stream_errorp, + CURLcode *resultp) +{ + char *newurl = NULL; + bool retry = FALSE; + timediff_t recv_timeout_ms = 0; + timediff_t send_timeout_ms = 0; + CURLMcode rc = CURLM_OK; + CURLcode result = *resultp = CURLE_OK; + *stream_errorp = FALSE; + + /* check if over send speed */ + if(data->set.max_send_speed) + send_timeout_ms = Curl_pgrsLimitWaitTime(&data->progress.ul, + data->set.max_send_speed, + *nowp); + + /* check if over recv speed */ + if(data->set.max_recv_speed) + recv_timeout_ms = Curl_pgrsLimitWaitTime(&data->progress.dl, + data->set.max_recv_speed, + *nowp); + + if(send_timeout_ms || recv_timeout_ms) { + Curl_ratelimit(data, *nowp); + multistate(data, MSTATE_RATELIMITING); + if(send_timeout_ms >= recv_timeout_ms) + Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); + else + Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); + return CURLM_OK; + } + + /* read/write data if it is ready to do so */ + result = Curl_sendrecv(data, nowp); + + if(data->req.done || (result == CURLE_RECV_ERROR)) { + /* If CURLE_RECV_ERROR happens early enough, we assume it was a race + * condition and the server closed the reused connection exactly when we + * wanted to use it, so figure out if that is indeed the case. + */ + CURLcode ret = Curl_retry_request(data, &newurl); + if(!ret) + retry = !!newurl; + else if(!result) + result = ret; + + if(retry) { + /* if we are to retry, set the result to OK and consider the + request as done */ + result = CURLE_OK; + data->req.done = TRUE; + } + } + else if((CURLE_HTTP2_STREAM == result) && + Curl_h2_http_1_1_error(data)) { + CURLcode ret = Curl_retry_request(data, &newurl); + + if(!ret) { + infof(data, "Downgrades to HTTP/1.1"); + streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1"); + data->state.httpwant = CURL_HTTP_VERSION_1_1; + /* clear the error message bit too as we ignore the one we got */ + data->state.errorbuf = FALSE; + if(!newurl) + /* typically for HTTP_1_1_REQUIRED error on first flight */ + newurl = strdup(data->state.url); + /* if we are to retry, set the result to OK and consider the request + as done */ + retry = TRUE; + result = CURLE_OK; + data->req.done = TRUE; + } + else + result = ret; + } + + if(result) { + /* + * The transfer phase returned error, we mark the connection to get closed + * to prevent being reused. This is because we cannot possibly know if the + * connection is in a good shape or not now. Unless it is a protocol which + * uses two "channels" like FTP, as then the error happened in the data + * connection. + */ + + if(!(data->conn->handler->flags & PROTOPT_DUAL) && + result != CURLE_HTTP2_STREAM) + streamclose(data->conn, "Transfer returned error"); + + multi_posttransfer(data); + multi_done(data, result, TRUE); + } + else if(data->req.done && !Curl_cwriter_is_paused(data)) { + + /* call this even if the readwrite function returned error */ + multi_posttransfer(data); + + /* When we follow redirects or is set to retry the connection, we must to + go back to the CONNECT state */ + if(data->req.newurl || retry) { + followtype follow = FOLLOW_NONE; + if(!retry) { + /* if the URL is a follow-location and not just a retried request then + figure out the URL here */ + free(newurl); + newurl = data->req.newurl; + data->req.newurl = NULL; + follow = FOLLOW_REDIR; + } + else + follow = FOLLOW_RETRY; + (void)multi_done(data, CURLE_OK, FALSE); + /* multi_done() might return CURLE_GOT_NOTHING */ + result = multi_follow(data, newurl, follow); + if(!result) { + multistate(data, MSTATE_SETUP); + rc = CURLM_CALL_MULTI_PERFORM; + } + } + else { + /* after the transfer is done, go DONE */ + + /* but first check to see if we got a location info even though we are + not following redirects */ + if(data->req.location) { + free(newurl); + newurl = data->req.location; + data->req.location = NULL; + result = multi_follow(data, newurl, FOLLOW_FAKE); + if(result) { + *stream_errorp = TRUE; + result = multi_done(data, result, TRUE); + } + } + + if(!result) { + multistate(data, MSTATE_DONE); + rc = CURLM_CALL_MULTI_PERFORM; + } + } + } + else if(data->state.select_bits && !Curl_xfer_is_blocked(data)) { + /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer does + not get stuck on this transfer at the expense of other concurrent + transfers */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + } + free(newurl); + *resultp = result; + return rc; +} + +static CURLMcode state_do(struct Curl_easy *data, + bool *stream_errorp, + CURLcode *resultp) +{ + CURLMcode rc = CURLM_OK; + CURLcode result = CURLE_OK; + if(data->set.fprereq) { + int prereq_rc; + + /* call the prerequest callback function */ + Curl_set_in_callback(data, TRUE); + prereq_rc = data->set.fprereq(data->set.prereq_userp, + data->info.primary.remote_ip, + data->info.primary.local_ip, + data->info.primary.remote_port, + data->info.primary.local_port); + Curl_set_in_callback(data, FALSE); + if(prereq_rc != CURL_PREREQFUNC_OK) { + failf(data, "operation aborted by pre-request callback"); + /* failure in pre-request callback - do not do any other processing */ + result = CURLE_ABORTED_BY_CALLBACK; + multi_posttransfer(data); + multi_done(data, result, FALSE); + *stream_errorp = TRUE; + goto end; + } + } + + if(data->set.connect_only == 1) { + /* keep connection open for application to use the socket */ + connkeep(data->conn, "CONNECT_ONLY"); + multistate(data, MSTATE_DONE); + rc = CURLM_CALL_MULTI_PERFORM; + } + else { + bool dophase_done = FALSE; + /* 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 + /* some steps needed for wildcard matching */ + if(data->state.wildcardmatch) { + struct WildcardData *wc = data->wildcard; + if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { + /* skip some states if it is important */ + multi_done(data, CURLE_OK, FALSE); + + /* if there is no connection left, skip the DONE state */ + multistate(data, data->conn ? + MSTATE_DONE : MSTATE_COMPLETED); + rc = CURLM_CALL_MULTI_PERFORM; + goto end; + } + } +#endif + /* DO was not completed in one function call, we must continue + DOING... */ + multistate(data, MSTATE_DOING); + rc = CURLM_CALL_MULTI_PERFORM; + } + + /* after DO, go DO_DONE... or DO_MORE */ + else if(data->conn->bits.do_more) { + /* we are supposed to do more, but we need to sit down, relax and wait + a little while first */ + multistate(data, MSTATE_DOING_MORE); + rc = CURLM_CALL_MULTI_PERFORM; + } + else { + /* we are done with the DO, now DID */ + multistate(data, MSTATE_DID); + rc = CURLM_CALL_MULTI_PERFORM; + } + } + else if((CURLE_SEND_ERROR == result) && + data->conn->bits.reuse) { + /* + * In this situation, a connection that we were trying to use may have + * unexpectedly died. If possible, send the connection back to the + * CONNECT phase so we can try again. + */ + char *newurl = NULL; + followtype follow = FOLLOW_NONE; + CURLcode drc; + + drc = Curl_retry_request(data, &newurl); + if(drc) { + /* a failure here pretty much implies an out of memory */ + result = drc; + *stream_errorp = TRUE; + } + + multi_posttransfer(data); + drc = multi_done(data, result, FALSE); + + /* When set to retry the connection, we must go back to the CONNECT + * state */ + if(newurl) { + if(!drc || (drc == CURLE_SEND_ERROR)) { + follow = FOLLOW_RETRY; + drc = multi_follow(data, newurl, follow); + if(!drc) { + multistate(data, MSTATE_SETUP); + rc = CURLM_CALL_MULTI_PERFORM; + result = CURLE_OK; + } + else { + /* Follow failed */ + result = drc; + } + } + else { + /* done did not return OK or SEND_ERROR */ + result = drc; + } + } + else { + /* Have error handler disconnect conn if we cannot retry */ + *stream_errorp = TRUE; + } + free(newurl); + } + else { + /* failure detected */ + multi_posttransfer(data); + if(data->conn) + multi_done(data, result, FALSE); + *stream_errorp = TRUE; + } + } +end: + *resultp = result; + return rc; +} + +static CURLMcode state_ratelimiting(struct Curl_easy *data, + struct curltime *nowp, + CURLcode *resultp) +{ + CURLcode result = CURLE_OK; + CURLMcode rc = CURLM_OK; + DEBUGASSERT(data->conn); + /* if both rates are within spec, resume transfer */ + if(Curl_pgrsUpdate(data)) + result = CURLE_ABORTED_BY_CALLBACK; + else + result = Curl_speedcheck(data, *nowp); + + if(result) { + if(!(data->conn->handler->flags & PROTOPT_DUAL) && + result != CURLE_HTTP2_STREAM) + streamclose(data->conn, "Transfer returned error"); + + multi_posttransfer(data); + multi_done(data, result, TRUE); + } + else { + timediff_t recv_timeout_ms = 0; + timediff_t send_timeout_ms = 0; + if(data->set.max_send_speed) + send_timeout_ms = + Curl_pgrsLimitWaitTime(&data->progress.ul, + data->set.max_send_speed, + *nowp); + + if(data->set.max_recv_speed) + recv_timeout_ms = + Curl_pgrsLimitWaitTime(&data->progress.dl, + data->set.max_recv_speed, + *nowp); + + if(!send_timeout_ms && !recv_timeout_ms) { + multistate(data, MSTATE_PERFORMING); + Curl_ratelimit(data, *nowp); + /* start performing again right away */ + rc = CURLM_CALL_MULTI_PERFORM; + } + else if(send_timeout_ms >= recv_timeout_ms) + Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); + else + Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); + } + *resultp = result; + return rc; +} + +static CURLMcode state_resolving(struct Curl_multi *multi, + struct Curl_easy *data, + bool *stream_errorp, + CURLcode *resultp) +{ + struct Curl_dns_entry *dns = NULL; + struct connectdata *conn = data->conn; + const char *hostname; + CURLcode result = CURLE_OK; + CURLMcode rc = CURLM_OK; + + DEBUGASSERT(conn); +#ifndef CURL_DISABLE_PROXY + if(conn->bits.httpproxy) + hostname = conn->http_proxy.host.name; + else +#endif + if(conn->bits.conn_to_host) + hostname = conn->conn_to_host.name; + else + hostname = conn->host.name; + + /* check if we have the name resolved by now */ + dns = Curl_fetch_addr(data, hostname, conn->primary.remote_port); + + if(dns) { +#ifdef CURLRES_ASYNCH + data->state.async.dns = dns; + data->state.async.done = TRUE; +#endif + result = CURLE_OK; + infof(data, "Hostname '%s' was found in DNS cache", hostname); + } + + if(!dns) + result = Curl_resolv_check(data, &dns); + + /* Update sockets here, because the socket(s) may have been closed and the + application thus needs to be told, even if it is likely that the same + socket(s) will again be used further down. If the name has not yet been + resolved, it is likely that new sockets have been opened in an attempt to + contact another resolver. */ + rc = singlesocket(multi, data); + if(rc) + return rc; + + if(dns) { + bool connected; + /* Perform the next step in the connection phase, and then move on to the + WAITCONNECT state */ + result = Curl_once_resolved(data, &connected); + + if(result) + /* if Curl_once_resolved() returns failure, the connection struct is + already freed and gone */ + data->conn = NULL; /* no more connection */ + else { + /* call again please so that we get the next socket setup */ + rc = CURLM_CALL_MULTI_PERFORM; + if(connected) + multistate(data, MSTATE_PROTOCONNECT); + else { + multistate(data, MSTATE_CONNECTING); + } + } + } + + if(result) + /* failure detected */ + *stream_errorp = TRUE; + + *resultp = result; + return rc; +} + +static CURLMcode state_connect(struct Curl_multi *multi, + struct Curl_easy *data, + struct curltime *nowp, + CURLcode *resultp) +{ + /* Connect. We want to get a connection identifier filled in. This state can + be entered from SETUP and from PENDING. */ + bool connected; + bool async; + CURLMcode rc = CURLM_OK; + CURLcode result = Curl_connect(data, &async, &connected); + if(CURLE_NO_CONNECTION_AVAILABLE == result) { + /* There was no connection available. We will go to the pending state and + wait for an available connection. */ + multistate(data, MSTATE_PENDING); + /* unlink from process list */ + Curl_node_remove(&data->multi_queue); + /* add handle to pending list */ + Curl_llist_append(&multi->pending, data, &data->multi_queue); + *resultp = CURLE_OK; + return rc; + } + else + process_pending_handles(data->multi); + + if(!result) { + *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE); + if(async) + /* We are now waiting for an asynchronous name lookup */ + multistate(data, MSTATE_RESOLVING); + else { + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to WAITDO or + DO! */ + rc = CURLM_CALL_MULTI_PERFORM; + + if(connected) { + if(!data->conn->bits.reuse && + Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { + /* new connection, can multiplex, wake pending handles */ + process_pending_handles(data->multi); + } + multistate(data, MSTATE_PROTOCONNECT); + } + else { + multistate(data, MSTATE_CONNECTING); + } + } + } + *resultp = result; + return rc; +} + static CURLMcode multi_runsingle(struct Curl_multi *multi, struct curltime *nowp, struct Curl_easy *data) { struct Curl_message *msg = NULL; bool connected; - bool async; bool protocol_connected = FALSE; bool dophase_done = FALSE; CURLMcode rc; CURLcode result = CURLE_OK; - timediff_t recv_timeout_ms; - timediff_t send_timeout_ms; int control; if(!GOOD_EASY_HANDLE(data)) @@ -1874,8 +2646,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, switch(data->mstate) { case MSTATE_INIT: - /* Transitional state. init this transfer. A handle never comes - back to this state. */ + /* Transitional state. init this transfer. A handle never comes back to + this state. */ result = Curl_pretransfer(data); if(result) break; @@ -1901,119 +2673,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, FALLTHROUGH(); case MSTATE_CONNECT: - /* Connect. We want to get a connection identifier filled in. This state - can be entered from SETUP and from PENDING. */ - result = Curl_connect(data, &async, &connected); - if(CURLE_NO_CONNECTION_AVAILABLE == result) { - /* There was no connection available. We will go to the pending - state and wait for an available connection. */ - multistate(data, MSTATE_PENDING); - /* unlink from process list */ - Curl_node_remove(&data->multi_queue); - /* add handle to pending list */ - Curl_llist_append(&multi->pending, data, &data->multi_queue); - result = CURLE_OK; - break; - } - else - process_pending_handles(data->multi); - - if(!result) { - *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE); - if(async) - /* We are now waiting for an asynchronous name lookup */ - multistate(data, MSTATE_RESOLVING); - else { - /* after the connect has been sent off, go WAITCONNECT unless the - protocol connect is already done and we can go directly to - WAITDO or DO! */ - rc = CURLM_CALL_MULTI_PERFORM; - - if(connected) { - if(!data->conn->bits.reuse && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); - } - multistate(data, MSTATE_PROTOCONNECT); - } - else { - multistate(data, MSTATE_CONNECTING); - } - } - } + rc = state_connect(multi, data, nowp, &result); break; case MSTATE_RESOLVING: /* awaiting an asynch name resolve to complete */ - { - struct Curl_dns_entry *dns = NULL; - struct connectdata *conn = data->conn; - const char *hostname; - - DEBUGASSERT(conn); -#ifndef CURL_DISABLE_PROXY - if(conn->bits.httpproxy) - hostname = conn->http_proxy.host.name; - else -#endif - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; - - /* check if we have the name resolved by now */ - dns = Curl_fetch_addr(data, hostname, conn->primary.remote_port); - - if(dns) { -#ifdef CURLRES_ASYNCH - data->state.async.dns = dns; - data->state.async.done = TRUE; -#endif - result = CURLE_OK; - infof(data, "Hostname '%s' was found in DNS cache", hostname); - } - - if(!dns) - result = Curl_resolv_check(data, &dns); - - /* Update sockets here, because the socket(s) may have been - closed and the application thus needs to be told, even if it - is likely that the same socket(s) will again be used further - down. If the name has not yet been resolved, it is likely - that new sockets have been opened in an attempt to contact - another resolver. */ - rc = singlesocket(multi, data); - if(rc) - return rc; - - if(dns) { - /* Perform the next step in the connection phase, and then move on - to the WAITCONNECT state */ - result = Curl_once_resolved(data, &connected); - - if(result) - /* if Curl_once_resolved() returns failure, the connection struct - is already freed and gone */ - data->conn = NULL; /* no more connection */ - else { - /* call again please so that we get the next socket setup */ - rc = CURLM_CALL_MULTI_PERFORM; - if(connected) - multistate(data, MSTATE_PROTOCONNECT); - else { - multistate(data, MSTATE_CONNECTING); - } - } - } - - if(result) { - /* failure detected */ - stream_error = TRUE; - break; - } - } - break; + rc = state_resolving(multi, data, &stream_error, &result); + break; #ifndef CURL_DISABLE_HTTP case MSTATE_TUNNELING: @@ -2054,9 +2720,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, 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. */ + /* 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); rc = CURLM_CALL_MULTI_PERFORM; break; @@ -2098,135 +2765,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case MSTATE_DO: - if(data->set.fprereq) { - int prereq_rc; - - /* call the prerequest callback function */ - Curl_set_in_callback(data, TRUE); - prereq_rc = data->set.fprereq(data->set.prereq_userp, - data->info.primary.remote_ip, - data->info.primary.local_ip, - data->info.primary.remote_port, - data->info.primary.local_port); - Curl_set_in_callback(data, FALSE); - if(prereq_rc != CURL_PREREQFUNC_OK) { - failf(data, "operation aborted by pre-request callback"); - /* failure in pre-request callback - do not do any other - processing */ - result = CURLE_ABORTED_BY_CALLBACK; - multi_posttransfer(data); - multi_done(data, result, FALSE); - stream_error = TRUE; - break; - } - } - - if(data->set.connect_only == 1) { - /* keep connection open for application to use the socket */ - connkeep(data->conn, "CONNECT_ONLY"); - multistate(data, MSTATE_DONE); - result = CURLE_OK; - rc = CURLM_CALL_MULTI_PERFORM; - } - else { - /* 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 - /* some steps needed for wildcard matching */ - if(data->state.wildcardmatch) { - struct WildcardData *wc = data->wildcard; - if(wc->state == CURLWC_DONE || wc->state == CURLWC_SKIP) { - /* skip some states if it is important */ - multi_done(data, CURLE_OK, FALSE); - - /* if there is no connection left, skip the DONE state */ - multistate(data, data->conn ? - MSTATE_DONE : MSTATE_COMPLETED); - rc = CURLM_CALL_MULTI_PERFORM; - break; - } - } -#endif - /* DO was not completed in one function call, we must continue - DOING... */ - multistate(data, MSTATE_DOING); - rc = CURLM_CALL_MULTI_PERFORM; - } - - /* after DO, go DO_DONE... or DO_MORE */ - else if(data->conn->bits.do_more) { - /* we are supposed to do more, but we need to sit down, relax - and wait a little while first */ - multistate(data, MSTATE_DOING_MORE); - rc = CURLM_CALL_MULTI_PERFORM; - } - else { - /* we are done with the DO, now DID */ - multistate(data, MSTATE_DID); - rc = CURLM_CALL_MULTI_PERFORM; - } - } - else if((CURLE_SEND_ERROR == result) && - data->conn->bits.reuse) { - /* - * In this situation, a connection that we were trying to use - * may have unexpectedly died. If possible, send the connection - * back to the CONNECT phase so we can try again. - */ - char *newurl = NULL; - followtype follow = FOLLOW_NONE; - CURLcode drc; - - drc = Curl_retry_request(data, &newurl); - if(drc) { - /* a failure here pretty much implies an out of memory */ - result = drc; - stream_error = TRUE; - } - - multi_posttransfer(data); - drc = multi_done(data, result, FALSE); - - /* When set to retry the connection, we must go back to the CONNECT - * state */ - if(newurl) { - if(!drc || (drc == CURLE_SEND_ERROR)) { - follow = FOLLOW_RETRY; - drc = Curl_follow(data, newurl, follow); - if(!drc) { - multistate(data, MSTATE_SETUP); - rc = CURLM_CALL_MULTI_PERFORM; - result = CURLE_OK; - } - else { - /* Follow failed */ - result = drc; - } - } - else { - /* done did not return OK or SEND_ERROR */ - result = drc; - } - } - else { - /* Have error handler disconnect conn if we cannot retry */ - stream_error = TRUE; - } - free(newurl); - } - else { - /* failure detected */ - multi_posttransfer(data); - if(data->conn) - multi_done(data, result, FALSE); - stream_error = TRUE; - } - } + rc = state_do(data, &stream_error, &result); break; case MSTATE_DOING: @@ -2299,195 +2838,12 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, break; case MSTATE_RATELIMITING: /* limit-rate exceeded in either direction */ - DEBUGASSERT(data->conn); - /* if both rates are within spec, resume transfer */ - if(Curl_pgrsUpdate(data)) - result = CURLE_ABORTED_BY_CALLBACK; - else - result = Curl_speedcheck(data, *nowp); - - if(result) { - if(!(data->conn->handler->flags & PROTOPT_DUAL) && - result != CURLE_HTTP2_STREAM) - streamclose(data->conn, "Transfer returned error"); - - multi_posttransfer(data); - multi_done(data, result, TRUE); - } - else { - send_timeout_ms = 0; - if(data->set.max_send_speed) - send_timeout_ms = - Curl_pgrsLimitWaitTime(&data->progress.ul, - data->set.max_send_speed, - *nowp); - - recv_timeout_ms = 0; - if(data->set.max_recv_speed) - recv_timeout_ms = - Curl_pgrsLimitWaitTime(&data->progress.dl, - data->set.max_recv_speed, - *nowp); - - if(!send_timeout_ms && !recv_timeout_ms) { - multistate(data, MSTATE_PERFORMING); - Curl_ratelimit(data, *nowp); - /* start performing again right away */ - rc = CURLM_CALL_MULTI_PERFORM; - } - else if(send_timeout_ms >= recv_timeout_ms) - Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); - else - Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); - } + rc = state_ratelimiting(data, nowp, &result); break; case MSTATE_PERFORMING: - { - char *newurl = NULL; - bool retry = FALSE; - /* check if over send speed */ - send_timeout_ms = 0; - if(data->set.max_send_speed) - send_timeout_ms = Curl_pgrsLimitWaitTime(&data->progress.ul, - data->set.max_send_speed, - *nowp); - - /* check if over recv speed */ - recv_timeout_ms = 0; - if(data->set.max_recv_speed) - recv_timeout_ms = Curl_pgrsLimitWaitTime(&data->progress.dl, - data->set.max_recv_speed, - *nowp); - - if(send_timeout_ms || recv_timeout_ms) { - Curl_ratelimit(data, *nowp); - multistate(data, MSTATE_RATELIMITING); - if(send_timeout_ms >= recv_timeout_ms) - Curl_expire(data, send_timeout_ms, EXPIRE_TOOFAST); - else - Curl_expire(data, recv_timeout_ms, EXPIRE_TOOFAST); - break; - } - - /* read/write data if it is ready to do so */ - result = Curl_sendrecv(data, nowp); - - if(data->req.done || (result == CURLE_RECV_ERROR)) { - /* If CURLE_RECV_ERROR happens early enough, we assume it was a race - * condition and the server closed the reused connection exactly when - * we wanted to use it, so figure out if that is indeed the case. - */ - CURLcode ret = Curl_retry_request(data, &newurl); - if(!ret) - retry = (newurl) ? TRUE : FALSE; - else if(!result) - result = ret; - - if(retry) { - /* if we are to retry, set the result to OK and consider the - request as done */ - result = CURLE_OK; - data->req.done = TRUE; - } - } - else if((CURLE_HTTP2_STREAM == result) && - Curl_h2_http_1_1_error(data)) { - CURLcode ret = Curl_retry_request(data, &newurl); - - if(!ret) { - infof(data, "Downgrades to HTTP/1.1"); - streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1"); - data->state.httpwant = CURL_HTTP_VERSION_1_1; - /* clear the error message bit too as we ignore the one we got */ - data->state.errorbuf = FALSE; - if(!newurl) - /* typically for HTTP_1_1_REQUIRED error on first flight */ - newurl = strdup(data->state.url); - /* if we are to retry, set the result to OK and consider the request - as done */ - retry = TRUE; - result = CURLE_OK; - data->req.done = TRUE; - } - else - result = ret; - } - - if(result) { - /* - * The transfer phase returned error, we mark the connection to get - * closed to prevent being reused. This is because we cannot possibly - * know if the connection is in a good shape or not now. Unless it is - * a protocol which uses two "channels" like FTP, as then the error - * happened in the data connection. - */ - - if(!(data->conn->handler->flags & PROTOPT_DUAL) && - result != CURLE_HTTP2_STREAM) - streamclose(data->conn, "Transfer returned error"); - - multi_posttransfer(data); - multi_done(data, result, TRUE); - } - else if(data->req.done && !Curl_cwriter_is_paused(data)) { - - /* call this even if the readwrite function returned error */ - multi_posttransfer(data); - - /* When we follow redirects or is set to retry the connection, we must - to go back to the CONNECT state */ - if(data->req.newurl || retry) { - followtype follow = FOLLOW_NONE; - if(!retry) { - /* if the URL is a follow-location and not just a retried request - then figure out the URL here */ - free(newurl); - newurl = data->req.newurl; - data->req.newurl = NULL; - follow = FOLLOW_REDIR; - } - else - follow = FOLLOW_RETRY; - (void)multi_done(data, CURLE_OK, FALSE); - /* multi_done() might return CURLE_GOT_NOTHING */ - result = Curl_follow(data, newurl, follow); - if(!result) { - multistate(data, MSTATE_SETUP); - rc = CURLM_CALL_MULTI_PERFORM; - } - } - else { - /* after the transfer is done, go DONE */ - - /* but first check to see if we got a location info even though we - are not following redirects */ - if(data->req.location) { - free(newurl); - newurl = data->req.location; - data->req.location = NULL; - result = Curl_follow(data, newurl, FOLLOW_FAKE); - if(result) { - stream_error = TRUE; - result = multi_done(data, result, TRUE); - } - } - - if(!result) { - multistate(data, MSTATE_DONE); - rc = CURLM_CALL_MULTI_PERFORM; - } - } - } - else if(data->state.select_bits && !Curl_xfer_is_blocked(data)) { - /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer - will not get stuck on this transfer at the expense of other - concurrent transfers */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } - free(newurl); + rc = state_performing(data, nowp, &stream_error, &result); break; - } case MSTATE_DONE: /* this state is highly transient, so run another loop after this */ @@ -2627,13 +2983,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, } -CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) +CURLMcode curl_multi_perform(CURLM *m, int *running_handles) { CURLMcode returncode = CURLM_OK; struct Curl_tree *t = NULL; struct curltime now = Curl_now(); struct Curl_llist_node *e; struct Curl_llist_node *n = NULL; + struct Curl_multi *multi = m; SIGPIPE_VARIABLE(pipe_st); if(!GOOD_MULTI_HANDLE(multi)) @@ -2719,16 +3076,15 @@ static void unlink_all_msgsent_handles(struct Curl_multi *multi) } } -CURLMcode curl_multi_cleanup(struct Curl_multi *multi) +CURLMcode curl_multi_cleanup(CURLM *m) { + struct Curl_multi *multi = m; if(GOOD_MULTI_HANDLE(multi)) { struct Curl_llist_node *e; struct Curl_llist_node *n; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; - multi->magic = 0; /* not good anymore */ - /* move the pending and msgsent entries back to process so that there is just one list to iterate over */ unlink_all_msgsent_handles(multi); @@ -2762,6 +3118,8 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) Curl_cpool_destroy(&multi->cpool); + multi->magic = 0; /* not good anymore */ + sockhash_destroy(&multi->sockhash); Curl_hash_destroy(&multi->proto_hash); Curl_hash_destroy(&multi->hostcache); @@ -2796,9 +3154,10 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi) * beyond. The current design is fully O(1). */ -CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue) +CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue) { struct Curl_message *msg; + struct Curl_multi *multi = m; *msgs_in_queue = 0; /* default to none */ @@ -3225,7 +3584,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi, else { /* Expire with out current now, so we will get it below when * asking the splaytree for expired transfers. */ - Curl_expire_ex(data, &mrc.now, 0, EXPIRE_RUN_NOW); + expire_ex(data, &mrc.now, 0, EXPIRE_RUN_NOW); } } } @@ -3261,12 +3620,13 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } #undef curl_multi_setopt -CURLMcode curl_multi_setopt(struct Curl_multi *multi, +CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...) { CURLMcode res = CURLM_OK; va_list param; unsigned long uarg; + struct Curl_multi *multi = m; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -3342,24 +3702,26 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi, /* we define curl_multi_socket() in the public multi.h header */ #undef curl_multi_socket -CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, - int *running_handles) +CURLMcode curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles) { + struct Curl_multi *multi = m; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; return multi_socket(multi, FALSE, s, 0, running_handles); } -CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, +CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s, int ev_bitmask, int *running_handles) { + struct Curl_multi *multi = m; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; return multi_socket(multi, FALSE, s, ev_bitmask, running_handles); } -CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) +CURLMcode curl_multi_socket_all(CURLM *m, int *running_handles) { + struct Curl_multi *multi = m; if(multi->in_callback) return CURLM_RECURSIVE_API_CALL; return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); @@ -3409,10 +3771,11 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, return CURLM_OK; } -CURLMcode curl_multi_timeout(struct Curl_multi *multi, +CURLMcode curl_multi_timeout(CURLM *m, long *timeout_ms) { struct curltime expire_time; + struct Curl_multi *multi = m; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -3561,20 +3924,9 @@ multi_addtimeout(struct Curl_easy *data, return CURLM_OK; } -/* - * Curl_expire() - * - * given a number of milliseconds from now to use to set the 'act before - * this'-time for the transfer, to be extracted by curl_multi_timeout() - * - * The timeout will be added to a queue of timeouts if it defines a moment in - * time that is later than the current head of queue. - * - * Expire replaces a former timeout using the same id if already set. - */ -static void Curl_expire_ex(struct Curl_easy *data, - const struct curltime *nowp, - timediff_t milli, expire_id id) +static void expire_ex(struct Curl_easy *data, + const struct curltime *nowp, + timediff_t milli, expire_id id) { struct Curl_multi *multi = data->multi; struct curltime *curr_expire = &data->state.expiretime; @@ -3632,10 +3984,21 @@ static void Curl_expire_ex(struct Curl_easy *data, &data->state.timenode); } +/* + * Curl_expire() + * + * given a number of milliseconds from now to use to set the 'act before + * this'-time for the transfer, to be extracted by curl_multi_timeout() + * + * The timeout will be added to a queue of timeouts if it defines a moment in + * time that is later than the current head of queue. + * + * Expire replaces a former timeout using the same id if already set. + */ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id) { struct curltime now = Curl_now(); - Curl_expire_ex(data, &now, milli, id); + expire_ex(data, &now, milli, id); } /* @@ -3689,10 +4052,11 @@ bool Curl_expire_clear(struct Curl_easy *data) return FALSE; } -CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s, +CURLMcode curl_multi_assign(CURLM *m, curl_socket_t s, void *hashp) { struct Curl_sh_entry *there = NULL; + struct Curl_multi *multi = m; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; @@ -3763,10 +4127,10 @@ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) return multi->max_concurrent_streams; } -struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi) +CURL **curl_multi_get_handles(CURLM *m) { - struct Curl_easy **a = malloc(sizeof(struct Curl_easy *) * - (multi->num_easy + 1)); + struct Curl_multi *multi = m; + CURL **a = malloc(sizeof(struct Curl_easy *) * (multi->num_easy + 1)); if(a) { unsigned int i = 0; struct Curl_llist_node *e; @@ -3887,6 +4251,51 @@ void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf) data->multi->xfer_ulbuf_borrowed = FALSE; } +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; + } + if(data->multi->xfer_sockbuf_borrowed) { + failf(data, "attempt to borrow xfer_sockbuf when already borrowed"); + return CURLE_AGAIN; + } + + if(data->multi->xfer_sockbuf && blen > data->multi->xfer_sockbuf_len) { + /* not large enough, get a new one */ + free(data->multi->xfer_sockbuf); + data->multi->xfer_sockbuf = NULL; + data->multi->xfer_sockbuf_len = 0; + } + + if(!data->multi->xfer_sockbuf) { + data->multi->xfer_sockbuf = malloc(blen); + if(!data->multi->xfer_sockbuf) { + failf(data, "could not allocate xfer_sockbuf of %zu bytes", blen); + return CURLE_OUT_OF_MEMORY; + } + data->multi->xfer_sockbuf_len = blen; + } + + data->multi->xfer_sockbuf_borrowed = TRUE; + *pbuf = data->multi->xfer_sockbuf; + return CURLE_OK; +} + +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; +} + static void multi_xfer_bufs_free(struct Curl_multi *multi) { DEBUGASSERT(multi); @@ -3896,6 +4305,9 @@ static void multi_xfer_bufs_free(struct Curl_multi *multi) Curl_safefree(multi->xfer_ulbuf); multi->xfer_ulbuf_len = 0; multi->xfer_ulbuf_borrowed = FALSE; + Curl_safefree(multi->xfer_sockbuf); + multi->xfer_sockbuf_len = 0; + multi->xfer_sockbuf_borrowed = FALSE; } struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi, diff --git a/lib/multihandle.h b/lib/multihandle.h index fef117c0672b31..9225d2d3f156b7 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -124,6 +124,9 @@ struct Curl_multi { /* buffer used for upload data, lazy initialized */ char *xfer_ulbuf; /* the actual buffer */ size_t xfer_ulbuf_len; /* the allocated length */ + /* buffer used for socket I/O operations, lazy initialized */ + char *xfer_sockbuf; /* the actual buffer */ + size_t xfer_sockbuf_len; /* the allocated length */ /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note the pluralis form, there can be more than one easy handle waiting on the @@ -181,6 +184,7 @@ struct Curl_multi { burn */ BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */ BIT(xfer_ulbuf_borrowed); /* xfer_ulbuf is currently being borrowed */ + BIT(xfer_sockbuf_borrowed); /* xfer_sockbuf is currently being borrowed */ #ifdef DEBUGBUILD BIT(warned); /* true after user warned of DEBUGBUILD */ #endif diff --git a/lib/multiif.h b/lib/multiif.h index e5872cd6dcf4fb..fd0e21519fa3f8 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -144,6 +144,30 @@ CURLcode Curl_multi_xfer_ulbuf_borrow(struct Curl_easy *data, */ void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf); +/** + * Borrow the socket scratch buffer from the multi, suitable + * for the given transfer `data`. The buffer may only be used for + * direct socket I/O operation by one connection at a time and MUST be + * returned to the multi before the I/O call returns. + * Pointers into the buffer remain only valid as long as it is borrowed. + * + * @param data the easy handle + * @param blen requested length of the buffer + * @param pbuf on return, the buffer to use or NULL on error + * @return CURLE_OK when buffer is available and is returned. + * CURLE_OUT_OF_MEMORy on failure to allocate the buffer, + * CURLE_FAILED_INIT if the easy handle is without multi. + * CURLE_AGAIN if the buffer is borrowed already. + */ +CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data, + size_t blen, char **pbuf); +/** + * Release the borrowed buffer. All references into the buffer become + * invalid after this. + * @param buf the buffer pointer borrowed for coding error checks. + */ +void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf); + /** * Get the transfer handle for the given id. Returns NULL if not found. */ diff --git a/lib/netrc.c b/lib/netrc.c index 3c0651dcce0a17..d5ee3c0fd564a8 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -31,7 +31,6 @@ #include #include "netrc.h" -#include "strtok.h" #include "strcase.h" #include "curl_get_line.h" @@ -49,226 +48,284 @@ enum host_lookup_state { MACDEF }; +enum found_state { + NONE, + LOGIN, + PASSWORD +}; + +#define FOUND_LOGIN 1 +#define FOUND_PASSWORD 2 + #define NETRC_FILE_MISSING 1 #define NETRC_FAILED -1 #define NETRC_SUCCESS 0 -#define MAX_NETRC_LINE 4096 +#define MAX_NETRC_LINE 16384 +#define MAX_NETRC_FILE (128*1024) +#define MAX_NETRC_TOKEN 4096 + +static CURLcode file2memory(const char *filename, struct dynbuf *filebuf) +{ + CURLcode result = CURLE_OK; + FILE *file = fopen(filename, FOPEN_READTEXT); + struct dynbuf linebuf; + Curl_dyn_init(&linebuf, MAX_NETRC_LINE); + + if(file) { + while(Curl_get_line(&linebuf, file)) { + const char *line = Curl_dyn_ptr(&linebuf); + /* skip comments on load */ + while(ISBLANK(*line)) + line++; + if(*line == '#') + continue; + result = Curl_dyn_add(filebuf, line); + if(result) + goto done; + } + } +done: + Curl_dyn_free(&linebuf); + if(file) + fclose(file); + return result; +} /* * Returns zero on success. */ -static int parsenetrc(const char *host, - char **loginp, +static int parsenetrc(struct store_netrc *store, + const char *host, + char **loginp, /* might point to a username */ char **passwordp, - char *netrcfile) + const char *netrcfile) { - FILE *file; int retcode = NETRC_FILE_MISSING; char *login = *loginp; - char *password = *passwordp; - bool specific_login = (login && *login != 0); - bool login_alloc = FALSE; - bool password_alloc = FALSE; + char *password = NULL; + bool specific_login = !!login; /* points to something */ enum host_lookup_state state = NOTHING; + enum found_state keyword = NONE; + unsigned char found = 0; /* login + password found bits, as they can come in + any order */ + bool our_login = FALSE; /* found our login name */ + bool done = FALSE; + char *netrcbuffer; + struct dynbuf token; + struct dynbuf *filebuf = &store->filebuf; + DEBUGASSERT(!*passwordp); + Curl_dyn_init(&token, MAX_NETRC_TOKEN); - char state_login = 0; /* Found a login keyword */ - char state_password = 0; /* Found a password keyword */ - bool state_our_login = TRUE; /* With specific_login, found *our* login - name (or login-less line) */ - - DEBUGASSERT(netrcfile); + if(!store->loaded) { + if(file2memory(netrcfile, filebuf)) + return NETRC_FAILED; + store->loaded = TRUE; + } - file = fopen(netrcfile, FOPEN_READTEXT); - if(file) { - bool done = FALSE; - struct dynbuf buf; - Curl_dyn_init(&buf, MAX_NETRC_LINE); + netrcbuffer = Curl_dyn_ptr(filebuf); - while(!done && Curl_get_line(&buf, file)) { - char *tok; + while(!done) { + char *tok = netrcbuffer; + while(tok && !done) { char *tok_end; bool quoted; - char *netrcbuffer = Curl_dyn_ptr(&buf); + Curl_dyn_reset(&token); + while(ISBLANK(*tok)) + tok++; + /* tok is first non-space letter */ if(state == MACDEF) { - if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r')) - state = NOTHING; - else - continue; + if((*tok == '\n') || (*tok == '\r')) + state = NOTHING; /* end of macro definition */ } - tok = netrcbuffer; - while(tok) { - while(ISBLANK(*tok)) - tok++; - /* tok is first non-space letter */ - if(!*tok || (*tok == '#')) - /* end of line or the rest is a comment */ - break; - /* leading double-quote means quoted string */ - quoted = (*tok == '\"'); + if(!*tok || (*tok == '\n')) + /* end of line */ + break; - tok_end = tok; - if(!quoted) { - while(!ISSPACE(*tok_end)) - tok_end++; - *tok_end = 0; + /* leading double-quote means quoted string */ + quoted = (*tok == '\"'); + + tok_end = tok; + if(!quoted) { + size_t len = 0; + while(!ISSPACE(*tok_end)) { + tok_end++; + len++; } - else { - bool escape = FALSE; - bool endquote = FALSE; - char *store = tok; - tok_end++; /* pass the leading quote */ - while(*tok_end) { - char s = *tok_end; - if(escape) { - escape = FALSE; - switch(s) { - case 'n': - s = '\n'; - break; - case 'r': - s = '\r'; - break; - case 't': - s = '\t'; - break; - } - } - else if(s == '\\') { - escape = TRUE; - tok_end++; - continue; - } - else if(s == '\"') { - tok_end++; /* pass the ending quote */ - endquote = TRUE; + if(!len || Curl_dyn_addn(&token, tok, len)) { + retcode = NETRC_FAILED; + goto out; + } + } + else { + bool escape = FALSE; + bool endquote = FALSE; + tok_end++; /* pass the leading quote */ + while(*tok_end) { + char s = *tok_end; + if(escape) { + escape = FALSE; + switch(s) { + case 'n': + s = '\n'; + break; + case 'r': + s = '\r'; + break; + case 't': + s = '\t'; break; } - *store++ = s; + } + else if(s == '\\') { + escape = TRUE; tok_end++; + continue; + } + else if(s == '\"') { + tok_end++; /* pass the ending quote */ + endquote = TRUE; + break; } - *store = 0; - if(escape || !endquote) { - /* bad syntax, get out */ + if(Curl_dyn_addn(&token, &s, 1)) { retcode = NETRC_FAILED; goto out; } + tok_end++; } - - if((login && *login) && (password && *password)) { - done = TRUE; - break; + if(escape || !endquote) { + /* bad syntax, get out */ + retcode = NETRC_FAILED; + goto out; } + } - switch(state) { - case NOTHING: - if(strcasecompare("macdef", tok)) { - /* Define a macro. A macro is defined with the specified name; its - contents begin with the next .netrc line and continue until a - null line (consecutive new-line characters) is encountered. */ - state = MACDEF; - } - else if(strcasecompare("machine", tok)) { - /* the next tok is the machine name, this is in itself the - delimiter that starts the stuff entered for this machine, - after this we need to search for 'login' and - 'password'. */ - state = HOSTFOUND; - } - else if(strcasecompare("default", tok)) { - state = HOSTVALID; - retcode = NETRC_SUCCESS; /* we did find our host */ - } - break; - case MACDEF: - if(!strlen(tok)) { - state = NOTHING; - } - break; - case HOSTFOUND: - if(strcasecompare(host, tok)) { - /* and yes, this is our host! */ - state = HOSTVALID; - retcode = NETRC_SUCCESS; /* we did find our host */ - } - else - /* not our host */ - state = NOTHING; - break; - case HOSTVALID: - /* we are now parsing sub-keywords concerning "our" host */ - if(state_login) { - if(specific_login) { - state_our_login = !Curl_timestrcmp(login, tok); - } - else if(!login || Curl_timestrcmp(login, tok)) { - if(login_alloc) { - free(login); - login_alloc = FALSE; - } - login = strdup(tok); - if(!login) { - retcode = NETRC_FAILED; /* allocation failed */ - goto out; - } - login_alloc = TRUE; - } - state_login = 0; - } - else if(state_password) { - if((state_our_login || !specific_login) - && (!password || Curl_timestrcmp(password, tok))) { - if(password_alloc) { - free(password); - password_alloc = FALSE; - } - password = strdup(tok); - if(!password) { - retcode = NETRC_FAILED; /* allocation failed */ - goto out; - } - password_alloc = TRUE; + tok = Curl_dyn_ptr(&token); + + switch(state) { + case NOTHING: + if(strcasecompare("macdef", tok)) + /* Define a macro. A macro is defined with the specified name; its + contents begin with the next .netrc line and continue until a + null line (consecutive new-line characters) is encountered. */ + state = MACDEF; + else if(strcasecompare("machine", tok)) { + /* the next tok is the machine name, this is in itself the delimiter + that starts the stuff entered for this machine, after this we + need to search for 'login' and 'password'. */ + state = HOSTFOUND; + keyword = NONE; + found = 0; + our_login = FALSE; + Curl_safefree(password); + if(!specific_login) + Curl_safefree(login); + } + else if(strcasecompare("default", tok)) { + state = HOSTVALID; + retcode = NETRC_SUCCESS; /* we did find our host */ + } + break; + case MACDEF: + if(!*tok) + state = NOTHING; + break; + case HOSTFOUND: + if(strcasecompare(host, tok)) { + /* and yes, this is our host! */ + state = HOSTVALID; + retcode = NETRC_SUCCESS; /* we did find our host */ + } + else + /* not our host */ + state = NOTHING; + break; + case HOSTVALID: + /* we are now parsing sub-keywords concerning "our" host */ + if(keyword == LOGIN) { + if(specific_login) + our_login = !Curl_timestrcmp(login, tok); + else { + our_login = TRUE; + free(login); + login = strdup(tok); + if(!login) { + retcode = NETRC_FAILED; /* allocation failed */ + goto out; } - state_password = 0; } - else if(strcasecompare("login", tok)) - state_login = 1; - else if(strcasecompare("password", tok)) - state_password = 1; - else if(strcasecompare("machine", tok)) { - /* ok, there is machine here go => */ - state = HOSTFOUND; - state_our_login = FALSE; + found |= FOUND_LOGIN; + keyword = NONE; + } + else if(keyword == PASSWORD) { + free(password); + password = strdup(tok); + if(!password) { + retcode = NETRC_FAILED; /* allocation failed */ + goto out; } + found |= FOUND_PASSWORD; + keyword = NONE; + } + else if(strcasecompare("login", tok)) + keyword = LOGIN; + else if(strcasecompare("password", tok)) + keyword = PASSWORD; + else if(strcasecompare("machine", tok)) { + /* a new machine here */ + state = HOSTFOUND; + keyword = NONE; + found = 0; + Curl_safefree(password); + if(!specific_login) + Curl_safefree(login); + } + else if(strcasecompare("default", tok)) { + state = HOSTVALID; + retcode = NETRC_SUCCESS; /* we did find our host */ + Curl_safefree(password); + if(!specific_login) + Curl_safefree(login); + } + if((found == (FOUND_PASSWORD|FOUND_LOGIN)) && our_login) { + done = TRUE; break; - } /* switch (state) */ - tok = ++tok_end; - } - } /* while Curl_get_line() */ - -out: - Curl_dyn_free(&buf); - if(!retcode) { - /* success */ - if(login_alloc) { - if(*loginp) - free(*loginp); - *loginp = login; - } - if(password_alloc) { - if(*passwordp) - free(*passwordp); - *passwordp = password; - } + } + break; + } /* switch (state) */ + tok = ++tok_end; } - else { - if(login_alloc) - free(login); - if(password_alloc) - free(password); + if(!done) { + char *nl = NULL; + if(tok) + nl = strchr(tok, '\n'); + if(!nl) + break; + /* point to next line */ + netrcbuffer = &nl[1]; } - fclose(file); + } /* while !done */ + +out: + Curl_dyn_free(&token); + if(!retcode && !password && our_login) { + /* success without a password, set a blank one */ + password = strdup(""); + if(!password) + retcode = 1; /* out of memory */ + } + if(!retcode) { + /* success */ + if(!specific_login) + *loginp = login; + *passwordp = password; + } + else { + Curl_dyn_free(filebuf); + if(!specific_login) + free(login); + free(password); } return retcode; @@ -280,7 +337,8 @@ static int parsenetrc(const char *host, * *loginp and *passwordp MUST be allocated if they are not NULL when passed * in. */ -int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, +int Curl_parsenetrc(struct store_netrc *store, const char *host, + char **loginp, char **passwordp, char *netrcfile) { int retcode = 1; @@ -329,7 +387,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, free(homea); return -1; } - retcode = parsenetrc(host, loginp, passwordp, filealloc); + retcode = parsenetrc(store, host, loginp, passwordp, filealloc); free(filealloc); #ifdef _WIN32 if(retcode == NETRC_FILE_MISSING) { @@ -339,15 +397,25 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp, free(homea); return -1; } - retcode = parsenetrc(host, loginp, passwordp, filealloc); + retcode = parsenetrc(store, host, loginp, passwordp, filealloc); free(filealloc); } #endif free(homea); } else - retcode = parsenetrc(host, loginp, passwordp, netrcfile); + retcode = parsenetrc(store, host, loginp, passwordp, netrcfile); return retcode; } +void Curl_netrc_init(struct store_netrc *s) +{ + Curl_dyn_init(&s->filebuf, MAX_NETRC_FILE); + s->loaded = FALSE; +} +void Curl_netrc_cleanup(struct store_netrc *s) +{ + Curl_dyn_free(&s->filebuf); + s->loaded = FALSE; +} #endif diff --git a/lib/netrc.h b/lib/netrc.h index 37c95db5e4e4cc..0ef9ff78ea2a0e 100644 --- a/lib/netrc.h +++ b/lib/netrc.h @@ -26,9 +26,19 @@ #include "curl_setup.h" #ifndef CURL_DISABLE_NETRC +#include "dynbuf.h" + +struct store_netrc { + struct dynbuf filebuf; + char *filename; + BIT(loaded); +}; + +void Curl_netrc_init(struct store_netrc *s); +void Curl_netrc_cleanup(struct store_netrc *s); /* returns -1 on failure, 0 if the host is found, 1 is the host is not found */ -int Curl_parsenetrc(const char *host, char **loginp, +int Curl_parsenetrc(struct store_netrc *s, const char *host, char **loginp, char **passwordp, char *filename); /* Assume: (*passwordp)[0]=0, host[0] != 0. * If (*loginp)[0] = 0, search for login and password within a machine @@ -38,6 +48,8 @@ int Curl_parsenetrc(const char *host, char **loginp, #else /* disabled */ #define Curl_parsenetrc(a,b,c,d,e,f) 1 +#define Curl_netrc_init(x) +#define Curl_netrc_cleanup(x) #endif #endif /* HEADER_CURL_NETRC_H */ diff --git a/lib/pop3.c b/lib/pop3.c index 1f5334d917d280..db6ec04c7bff40 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -504,7 +504,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data, } /* Create the digest */ - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -1119,7 +1119,7 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done) } result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE); - *done = (pop3c->state == POP3_STOP) ? TRUE : FALSE; + *done = (pop3c->state == POP3_STOP); return result; } diff --git a/lib/progress.c b/lib/progress.c index 265abb1b572462..d3a1b9ac9afad1 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -381,6 +381,11 @@ void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size) } } +void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent) +{ + data->progress.earlydata_sent = sent; +} + /* returns the average speed in bytes / second */ static curl_off_t trspeed(curl_off_t size, /* number of bytes */ curl_off_t us) /* microseconds */ diff --git a/lib/progress.h b/lib/progress.h index 04a8f5bce969a8..326271ef1e4b31 100644 --- a/lib/progress.h +++ b/lib/progress.h @@ -69,6 +69,8 @@ timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d, void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer, struct curltime timestamp); +void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent); + #define PGRS_HIDE (1<<4) #define PGRS_UL_SIZE_KNOWN (1<<5) #define PGRS_DL_SIZE_KNOWN (1<<6) diff --git a/lib/psl.h b/lib/psl.h index 23cfa921c4bcf2..dd5bee21fbc14b 100644 --- a/lib/psl.h +++ b/lib/psl.h @@ -27,6 +27,8 @@ #ifdef USE_LIBPSL #include +struct Curl_easy; + #define PSL_TTL (72 * 3600) /* PSL time to live before a refresh. */ struct PslCache { diff --git a/lib/setopt.c b/lib/setopt.c index 0ae8af4e982ff1..ba80644bc73279 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -210,21 +210,58 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) return CURLE_OK; } -/* - * Do not make Curl_vsetopt() static: it is called from - * packages/OS400/ccsidcurl.c. - */ -CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +static CURLcode httpauth(struct Curl_easy *data, bool proxy, + unsigned long auth) { - char *argptr; - CURLcode result = CURLE_OK; - long arg; - unsigned long uarg; - curl_off_t bigsize; + 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 */ + } + + /* switch off bits we cannot support */ +#ifndef USE_NTLM + auth &= ~CURLAUTH_NTLM; /* no NTLM support */ +#endif +#ifndef USE_SPNEGO + auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without GSS-API + or SSPI */ +#endif + + /* check if any auth bit lower than CURLAUTH_ONLY is still set */ + while(bitcheck < 31) { + if(auth & (1UL << bitcheck++)) { + authbits = TRUE; + break; + } + } + if(!authbits) + return CURLE_NOT_BUILT_IN; /* no supported types left! */ + } + if(proxy) + data->set.proxyauth = auth; + else + data->set.httpauth = auth; + return CURLE_OK; +} +static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, + long arg) +{ + bool enabled = (0 != arg); + unsigned long uarg = (unsigned long)arg; switch(option) { case CURLOPT_DNS_CACHE_TIMEOUT: - arg = va_arg(param, long); if(arg < -1) return CURLE_BAD_FUNCTION_ARGUMENT; else if(arg > INT_MAX) @@ -234,7 +271,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) break; case CURLOPT_CA_CACHE_TIMEOUT: if(Curl_ssl_supports(data, SSLSUPP_CA_CACHE)) { - arg = va_arg(param, long); if(arg < -1) return CURLE_BAD_FUNCTION_ARGUMENT; else if(arg > INT_MAX) @@ -245,95 +281,47 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) else return CURLE_NOT_BUILT_IN; break; - case CURLOPT_DNS_USE_GLOBAL_CACHE: - /* deprecated */ - break; - case CURLOPT_SSL_CIPHER_LIST: - if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { - /* set a list of cipher we want to use in the SSL connection */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], - va_arg(param, char *)); - } - else - return CURLE_NOT_BUILT_IN; - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_CIPHER_LIST: - if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { - /* set a list of cipher we want to use in the SSL connection for proxy */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY], - va_arg(param, char *)); - } - else - return CURLE_NOT_BUILT_IN; - break; -#endif - case CURLOPT_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { - /* set preferred list of TLS 1.3 cipher suites */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST], - va_arg(param, char *)); - } - else - return CURLE_NOT_BUILT_IN; - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_TLS13_CIPHERS: - if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { - /* set preferred list of TLS 1.3 cipher suites for proxy */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST_PROXY], - va_arg(param, char *)); - } - else - return CURLE_NOT_BUILT_IN; - break; -#endif - case CURLOPT_RANDOM_FILE: - break; - case CURLOPT_EGDSOCKET: - break; case CURLOPT_MAXCONNECTS: /* * Set the absolute number of maximum simultaneous alive connection that * libcurl is allowed to have. */ - uarg = va_arg(param, unsigned long); if(uarg > UINT_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.maxconnects = (unsigned int)uarg; break; - case CURLOPT_FORBID_REUSE: + case CURLOPT_FORBID_REUSE: /* * When this transfer is done, it must not be left to be reused by a * subsequent transfer but shall be closed immediately. */ - data->set.reuse_forbid = (0 != va_arg(param, long)); + data->set.reuse_forbid = enabled; break; case CURLOPT_FRESH_CONNECT: /* * This transfer shall not use a previously cached connection but * should be made with a fresh new connect! */ - data->set.reuse_fresh = (0 != va_arg(param, long)); + data->set.reuse_fresh = enabled; break; case CURLOPT_VERBOSE: /* * Verbose means infof() calls that give a lot of information about * the connection and transfer procedures as well as internal choices. */ - data->set.verbose = (0 != va_arg(param, long)); + data->set.verbose = enabled; break; case CURLOPT_HEADER: /* * Set to include the header in the general data output stream. */ - data->set.include_header = (0 != va_arg(param, long)); + data->set.include_header = enabled; break; case CURLOPT_NOPROGRESS: /* * Shut off the internal supported progress meter */ - data->set.hide_progress = (0 != va_arg(param, long)); + data->set.hide_progress = enabled; if(data->set.hide_progress) data->progress.flags |= PGRS_HIDE; else @@ -343,7 +331,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Do not include the body part in the output data stream. */ - data->set.opt_no_body = (0 != va_arg(param, long)); + data->set.opt_no_body = enabled; #ifndef CURL_DISABLE_HTTP if(data->set.opt_no_body) /* in HTTP lingo, no body means using the HEAD request... */ @@ -357,10 +345,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Do not output the >=400 error code HTML-page, but instead only * return error. */ - data->set.http_fail_on_error = (0 != va_arg(param, long)); + data->set.http_fail_on_error = enabled; break; case CURLOPT_KEEP_SENDING_ON_ERROR: - data->set.http_keep_sending_on_error = (0 != va_arg(param, long)); + data->set.http_keep_sending_on_error = enabled; break; case CURLOPT_UPLOAD: case CURLOPT_PUT: @@ -368,7 +356,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * We want to sent data to the remote host. If this is HTTP, that equals * using the PUT request. */ - arg = va_arg(param, long); if(arg) { /* If this is HTTP, PUT is what's needed to "upload" */ data->set.method = HTTPREQ_PUT; @@ -379,23 +366,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) then this can be changed to HEAD later on) */ data->set.method = HTTPREQ_GET; break; - case CURLOPT_REQUEST_TARGET: - result = Curl_setstropt(&data->set.str[STRING_TARGET], - va_arg(param, char *)); - break; case CURLOPT_FILETIME: /* * Try to get the file time of the remote document. The time will * later (possibly) become available using curl_easy_getinfo(). */ - data->set.get_filetime = (0 != va_arg(param, long)); + data->set.get_filetime = enabled; break; case CURLOPT_SERVER_RESPONSE_TIMEOUT: /* * Option that specifies how quickly a server response must be obtained * before it is considered failure. For pingpong protocols. */ - arg = va_arg(param, long); if((arg >= 0) && (arg <= (INT_MAX/1000))) data->set.server_response_timeout = (unsigned int)arg * 1000; else @@ -406,7 +388,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Option that specifies how quickly a server response must be obtained * before it is considered failure. For pingpong protocols. */ - arg = va_arg(param, long); if((arg >= 0) && (arg <= INT_MAX)) data->set.server_response_timeout = (unsigned int)arg; else @@ -418,13 +399,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Option that prevents libcurl from sending TFTP option requests to the * server. */ - data->set.tftp_no_options = va_arg(param, long) != 0; + data->set.tftp_no_options = enabled; break; case CURLOPT_TFTP_BLKSIZE: /* * TFTP option that specifies the block size to use for data transmission. */ - arg = va_arg(param, long); if(arg < TFTP_BLKSIZE_MIN) arg = 512; else if(arg > TFTP_BLKSIZE_MAX) @@ -437,18 +417,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* * Parse the $HOME/.netrc file */ - arg = va_arg(param, long); if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.use_netrc = (unsigned char)arg; break; - case CURLOPT_NETRC_FILE: - /* - * Use this file instead of the $HOME/.netrc file - */ - result = Curl_setstropt(&data->set.str[STRING_NETRC_FILE], - va_arg(param, char *)); - break; #endif case CURLOPT_TRANSFERTEXT: /* @@ -457,14 +429,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * * Transfer using ASCII (instead of BINARY). */ - data->set.prefer_ascii = (0 != va_arg(param, long)); + data->set.prefer_ascii = enabled; break; case CURLOPT_TIMECONDITION: /* * Set HTTP time condition. This must be one of the defines in the * curl/curl.h header file. */ - arg = va_arg(param, long); if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.timecondition = (unsigned char)(curl_TimeCond)arg; @@ -474,17 +445,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * This is the value to compare with the remote document with the * method set with CURLOPT_TIMECONDITION */ - data->set.timevalue = (time_t)va_arg(param, long); - break; - - case CURLOPT_TIMEVALUE_LARGE: - /* - * This is the value to compare with the remote document with the - * method set with CURLOPT_TIMECONDITION - */ - data->set.timevalue = (time_t)va_arg(param, curl_off_t); + data->set.timevalue = (time_t)arg; break; - case CURLOPT_SSLVERSION: #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_SSLVERSION: @@ -501,9 +463,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(option != CURLOPT_SSLVERSION) primary = &data->set.proxy_ssl.primary; #endif - - arg = va_arg(param, long); - version = C_SSLVERSION_VALUE(arg); version_max = (long)C_SSLVERSION_MAX_VALUE(arg); @@ -519,136 +478,54 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) primary->version_max = (unsigned int)version_max; } #else - result = CURLE_NOT_BUILT_IN; + return CURLE_NOT_BUILT_IN; #endif break; - - /* MQTT "borrows" some of the HTTP options */ -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) - case CURLOPT_COPYPOSTFIELDS: - /* - * A string with POST data. Makes curl HTTP POST. Even if it is NULL. - * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to - * CURLOPT_COPYPOSTFIELDS and not altered later. - */ - argptr = va_arg(param, char *); - - if(!argptr || data->set.postfieldsize == -1) - result = Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr); - else { - /* - * Check that requested length does not overflow the size_t type. - */ - - if((data->set.postfieldsize < 0) || - ((sizeof(curl_off_t) != sizeof(size_t)) && - (data->set.postfieldsize > (curl_off_t)((size_t)-1)))) - result = CURLE_OUT_OF_MEMORY; - else { - /* Allocate even when size == 0. This satisfies the need of possible - later address compare to detect the COPYPOSTFIELDS mode, and to - mark that postfields is used rather than read function or form - data. - */ - char *p = Curl_memdup0(argptr, (size_t)data->set.postfieldsize); - if(!p) - result = CURLE_OUT_OF_MEMORY; - else { - free(data->set.str[STRING_COPYPOSTFIELDS]); - data->set.str[STRING_COPYPOSTFIELDS] = p; - } - } - } - - data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; - data->set.method = HTTPREQ_POST; - break; - - case CURLOPT_POSTFIELDS: - /* - * Like above, but use static data instead of copying it. - */ - data->set.postfields = va_arg(param, void *); - /* Release old copied data. */ - Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); - data->set.method = HTTPREQ_POST; - break; - case CURLOPT_POSTFIELDSIZE: /* * The size of the POSTFIELD data to prevent libcurl to do strlen() to * figure it out. Enables binary posts. */ - bigsize = va_arg(param, long); - if(bigsize < -1) + if(arg < -1) return CURLE_BAD_FUNCTION_ARGUMENT; - if(data->set.postfieldsize < bigsize && + if(data->set.postfieldsize < arg && data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); data->set.postfields = NULL; } - data->set.postfieldsize = bigsize; + data->set.postfieldsize = arg; break; - - case CURLOPT_POSTFIELDSIZE_LARGE: +#ifndef CURL_DISABLE_HTTP +#if !defined(CURL_DISABLE_COOKIES) + case CURLOPT_COOKIESESSION: /* - * The size of the POSTFIELD data to prevent libcurl to do strlen() to - * figure it out. Enables binary posts. + * Set this option to TRUE to start a new "cookie session". It will + * prevent the forthcoming read-cookies-from-file actions to accept + * cookies that are marked as being session cookies, as they belong to a + * previous session. */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - - if(data->set.postfieldsize < bigsize && - data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); - data->set.postfields = NULL; - } - - data->set.postfieldsize = bigsize; + data->set.cookiesession = enabled; break; #endif -#ifndef CURL_DISABLE_HTTP case CURLOPT_AUTOREFERER: /* * Switch on automatic referer that gets set if curl follows locations. */ - data->set.http_auto_referer = (0 != va_arg(param, long)); - break; - - case CURLOPT_ACCEPT_ENCODING: - /* - * String to use at the value of Accept-Encoding header. - * - * If the encoding is set to "" we use an Accept-Encoding header that - * encompasses all the encodings we support. - * If the encoding is set to NULL we do not send an Accept-Encoding header - * and ignore an received Content-Encoding header. - * - */ - argptr = va_arg(param, char *); - if(argptr && !*argptr) { - char all[256]; - Curl_all_content_encodings(all, sizeof(all)); - result = Curl_setstropt(&data->set.str[STRING_ENCODING], all); - } - else - result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr); + data->set.http_auto_referer = enabled; break; case CURLOPT_TRANSFER_ENCODING: - data->set.http_transfer_encoding = (0 != va_arg(param, long)); + data->set.http_transfer_encoding = enabled; break; case CURLOPT_FOLLOWLOCATION: /* * Follow Location: header hints on an HTTP-server. */ - data->set.http_follow_location = (0 != va_arg(param, long)); + data->set.http_follow_location = enabled; break; case CURLOPT_UNRESTRICTED_AUTH: @@ -656,7 +533,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Send authentication (user+password) when following locations, even when * hostname changed. */ - data->set.allow_auth_to_other_hosts = (0 != va_arg(param, long)); + data->set.allow_auth_to_other_hosts = enabled; break; case CURLOPT_MAXREDIRS: @@ -664,7 +541,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * The maximum amount of hops you allow curl to follow Location: * headers. This should mostly be used to detect never-ending loops. */ - arg = va_arg(param, long); if(arg < -1) return CURLE_BAD_FUNCTION_ARGUMENT; data->set.maxredirs = arg; @@ -680,7 +556,6 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303 * other - POST is kept as POST after 301 and 302 */ - arg = va_arg(param, long); if(arg < CURL_REDIR_GET_ALL) /* no return error on too high numbers since the bitmask could be extended in a future */ @@ -692,887 +567,904 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) /* Does this option serve a purpose anymore? Yes it does, when CURLOPT_POSTFIELDS is not used and the POST data is read off the callback! */ - if(va_arg(param, long)) { + if(arg) { data->set.method = HTTPREQ_POST; data->set.opt_no_body = FALSE; /* this is implied */ } else data->set.method = HTTPREQ_GET; break; - -#ifndef CURL_DISABLE_FORM_API - case CURLOPT_HTTPPOST: + case CURLOPT_HEADEROPT: /* - * Set to make us do HTTP POST. Legacy API-style. + * Set header option. */ - data->set.httppost = va_arg(param, struct curl_httppost *); - data->set.method = HTTPREQ_POST_FORM; - data->set.opt_no_body = FALSE; /* this is implied */ - Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); - data->state.mimepost = NULL; + data->set.sep_headers = !!(arg & CURLHEADER_SEPARATE); break; -#endif + case CURLOPT_HTTPAUTH: + return httpauth(data, FALSE, uarg); -#if !defined(CURL_DISABLE_AWS) - case CURLOPT_AWS_SIGV4: - /* - * String that is merged to some authentication - * parameters are used by the algorithm. - */ - result = Curl_setstropt(&data->set.str[STRING_AWS_SIGV4], - va_arg(param, char *)); + case CURLOPT_HTTPGET: /* - * Basic been set by default it need to be unset here + * Set to force us do HTTP GET */ - if(data->set.str[STRING_AWS_SIGV4]) - data->set.httpauth = CURLAUTH_AWS_SIGV4; + if(enabled) { + data->set.method = HTTPREQ_GET; + data->set.opt_no_body = FALSE; /* this is implied */ + } break; -#endif - case CURLOPT_REFERER: + case CURLOPT_HTTP_VERSION: /* - * String to set in the HTTP Referer: field. + * This sets a requested HTTP version to be used. The value is one of + * the listed enums in curl/curl.h. */ - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; + switch(arg) { + case CURL_HTTP_VERSION_NONE: +#ifdef USE_HTTP2 + /* TODO: this seems an undesirable quirk to force a behaviour on + * lower implementations that they should recognize independently? */ + arg = CURL_HTTP_VERSION_2TLS; +#endif + /* accepted */ + break; + case CURL_HTTP_VERSION_1_0: + case CURL_HTTP_VERSION_1_1: + /* accepted */ + break; +#ifdef USE_HTTP2 + case CURL_HTTP_VERSION_2_0: + case CURL_HTTP_VERSION_2TLS: + case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE: + /* accepted */ + break; +#endif +#ifdef USE_HTTP3 + case CURL_HTTP_VERSION_3: + case CURL_HTTP_VERSION_3ONLY: + /* accepted */ + break; +#endif + default: + /* not accepted */ + if(arg < CURL_HTTP_VERSION_NONE) + return CURLE_BAD_FUNCTION_ARGUMENT; + return CURLE_UNSUPPORTED_PROTOCOL; } - result = Curl_setstropt(&data->set.str[STRING_SET_REFERER], - va_arg(param, char *)); - data->state.referer = data->set.str[STRING_SET_REFERER]; + data->set.httpwant = (unsigned char)arg; break; - case CURLOPT_USERAGENT: + case CURLOPT_EXPECT_100_TIMEOUT_MS: /* - * String to use in the HTTP User-Agent field + * Time to wait for a response to an HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ - result = Curl_setstropt(&data->set.str[STRING_USERAGENT], - va_arg(param, char *)); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.expect_100_timeout = arg; break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYHEADER: - /* - * Set a list with proxy headers to use (or replace internals with) - * - * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a - * long time we remain doing it this way until CURLOPT_PROXYHEADER is - * used. As soon as this option has been used, if set to anything but - * NULL, custom headers for proxies are only picked from this list. - * - * Set this option to NULL to restore the previous behavior. - */ - data->set.proxyheaders = va_arg(param, struct curl_slist *); + case CURLOPT_HTTP09_ALLOWED: +#ifdef USE_HYPER + /* Hyper does not support HTTP/0.9 */ + if(enabled) + return CURLE_BAD_FUNCTION_ARGUMENT; +#else + data->set.http09_allowed = enabled; +#endif + break; +#endif /* ! CURL_DISABLE_HTTP */ + +#ifndef CURL_DISABLE_MIME + case CURLOPT_MIME_OPTIONS: + data->set.mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); break; #endif - case CURLOPT_HEADEROPT: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_HTTPPROXYTUNNEL: /* - * Set header option. + * Tunnel operations through the proxy instead of normal proxy use */ - arg = va_arg(param, long); - data->set.sep_headers = !!(arg & CURLHEADER_SEPARATE); + data->set.tunnel_thru_httpproxy = enabled; break; -#if !defined(CURL_DISABLE_COOKIES) - case CURLOPT_COOKIE: + case CURLOPT_PROXYPORT: /* - * Cookie string to send to the remote server in the request. + * Explicitly set HTTP proxy port number. */ - result = Curl_setstropt(&data->set.str[STRING_COOKIE], - va_arg(param, char *)); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.proxyport = (unsigned short)arg; break; - case CURLOPT_COOKIEFILE: + case CURLOPT_PROXYAUTH: + return httpauth(data, TRUE, uarg); + + case CURLOPT_PROXYTYPE: /* - * Set cookie file to read and parse. Can be used multiple times. + * Set proxy type. */ - argptr = (char *)va_arg(param, void *); - if(argptr) { - struct curl_slist *cl; - /* general protection against mistakes and abuse */ - if(strlen(argptr) > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - /* append the cookie filename to the list of filenames, and deal with - them later */ - cl = curl_slist_append(data->state.cookielist, argptr); - if(!cl) { - curl_slist_free_all(data->state.cookielist); - data->state.cookielist = NULL; - return CURLE_OUT_OF_MEMORY; - } - data->state.cookielist = cl; /* store the list for later use */ - } - else { - /* clear the list of cookie files */ - curl_slist_free_all(data->state.cookielist); - data->state.cookielist = NULL; - - if(!data->share || !data->share->cookies) { - /* throw away all existing cookies if this is not a shared cookie - container */ - Curl_cookie_clearall(data->cookies); - Curl_cookie_cleanup(data->cookies); - } - /* disable the cookie engine */ - data->cookies = NULL; - } + if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.proxytype = (unsigned char)(curl_proxytype)arg; break; - case CURLOPT_COOKIEJAR: + case CURLOPT_PROXY_TRANSFER_MODE: /* - * Set cookie filename to dump all cookies to when we are done. + * set transfer mode (;type=) when doing FTP via an HTTP proxy */ - result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR], - va_arg(param, char *)); - if(!result) { - /* - * Activate the cookie parser. This may or may not already - * have been made. - */ - struct CookieInfo *newcookies = - Curl_cookie_init(data, NULL, data->cookies, data->set.cookiesession); - if(!newcookies) - result = CURLE_OUT_OF_MEMORY; - data->cookies = newcookies; - } + if(uarg > 1) + /* reserve other values for future use */ + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.proxy_transfer_mode = (bool)uarg; break; - - case CURLOPT_COOKIESESSION: + case CURLOPT_SOCKS5_AUTH: + if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) + return CURLE_NOT_BUILT_IN; + data->set.socks5auth = (unsigned char)uarg; + break; + case CURLOPT_HAPROXYPROTOCOL: /* - * Set this option to TRUE to start a new "cookie session". It will - * prevent the forthcoming read-cookies-from-file actions to accept - * cookies that are marked as being session cookies, as they belong to a - * previous session. + * Set to send the HAProxy Proxy Protocol header */ - data->set.cookiesession = (0 != va_arg(param, long)); + data->set.haproxyprotocol = enabled; break; + case CURLOPT_PROXY_SSL_VERIFYPEER: + /* + * Enable peer SSL verifying for proxy. + */ + data->set.proxy_ssl.primary.verifypeer = enabled; - case CURLOPT_COOKIELIST: - argptr = va_arg(param, char *); - - if(!argptr) - break; - - if(strcasecompare(argptr, "ALL")) { - /* clear all cookies */ - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_clearall(data->cookies); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - else if(strcasecompare(argptr, "SESS")) { - /* clear session cookies */ - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_clearsess(data->cookies); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - } - else if(strcasecompare(argptr, "FLUSH")) { - /* flush cookies to file, takes care of the locking */ - Curl_flush_cookies(data, FALSE); - } - else if(strcasecompare(argptr, "RELOAD")) { - /* reload cookies from file */ - Curl_cookie_loadfiles(data); - break; - } - else { - if(!data->cookies) - /* if cookie engine was not running, activate it */ - data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); - - /* general protection against mistakes and abuse */ - if(strlen(argptr) > CURL_MAX_INPUT_LENGTH) - return CURLE_BAD_FUNCTION_ARGUMENT; - argptr = strdup(argptr); - if(!argptr || !data->cookies) { - result = CURLE_OUT_OF_MEMORY; - free(argptr); - } - else { - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - - if(checkprefix("Set-Cookie:", argptr)) - /* HTTP Header format line */ - Curl_cookie_add(data, data->cookies, TRUE, FALSE, argptr + 11, NULL, - NULL, TRUE); - - else - /* Netscape format line */ - Curl_cookie_add(data, data->cookies, FALSE, FALSE, argptr, NULL, - NULL, TRUE); - - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - free(argptr); - } - } - + /* Update the current connection proxy_ssl_config. */ + Curl_ssl_conn_config_update(data, TRUE); break; -#endif /* !CURL_DISABLE_COOKIES */ - - case CURLOPT_HTTPGET: + case CURLOPT_PROXY_SSL_VERIFYHOST: /* - * Set to force us do HTTP GET + * Enable verification of the hostname in the peer certificate for proxy */ - if(va_arg(param, long)) { - data->set.method = HTTPREQ_GET; - data->set.opt_no_body = FALSE; /* this is implied */ - } + data->set.proxy_ssl.primary.verifyhost = enabled; + + /* Update the current connection proxy_ssl_config. */ + Curl_ssl_conn_config_update(data, TRUE); break; +#endif /* ! CURL_DISABLE_PROXY */ - case CURLOPT_HTTP_VERSION: +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) + case CURLOPT_SOCKS5_GSSAPI_NEC: /* - * This sets a requested HTTP version to be used. The value is one of - * the listed enums in curl/curl.h. + * Set flag for NEC SOCK5 support */ - arg = va_arg(param, long); - switch(arg) { - case CURL_HTTP_VERSION_NONE: -#ifdef USE_HTTP2 - /* TODO: this seems an undesirable quirk to force a behaviour on - * lower implementations that they should recognize independently? */ - arg = CURL_HTTP_VERSION_2TLS; -#endif - /* accepted */ - break; - case CURL_HTTP_VERSION_1_0: - case CURL_HTTP_VERSION_1_1: - /* accepted */ - break; -#ifdef USE_HTTP2 - case CURL_HTTP_VERSION_2_0: - case CURL_HTTP_VERSION_2TLS: - case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE: - /* accepted */ - break; + data->set.socks5_gssapi_nec = enabled; + break; #endif -#ifdef USE_HTTP3 - case CURL_HTTP_VERSION_3: - case CURL_HTTP_VERSION_3ONLY: - /* accepted */ - break; +#ifdef CURL_LIST_ONLY_PROTOCOL + case CURLOPT_DIRLISTONLY: + /* + * An option that changes the command to one that asks for a list only, no + * file info details. Used for FTP, POP3 and SFTP. + */ + data->set.list_only = enabled; + break; #endif - default: - /* not accepted */ - if(arg < CURL_HTTP_VERSION_NONE) - return CURLE_BAD_FUNCTION_ARGUMENT; - return CURLE_UNSUPPORTED_PROTOCOL; - } - data->set.httpwant = (unsigned char)arg; + case CURLOPT_APPEND: + /* + * We want to upload and append to an existing file. Used for FTP and + * SFTP. + */ + data->set.remote_append = enabled; break; - case CURLOPT_EXPECT_100_TIMEOUT_MS: +#ifndef CURL_DISABLE_FTP + case CURLOPT_FTP_FILEMETHOD: /* - * Time to wait for a response to an HTTP request containing an - * Expect: 100-continue header before sending the data anyway. + * How do access files over FTP. */ - arg = va_arg(param, long); - if(arg < 0) + if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.expect_100_timeout = arg; + data->set.ftp_filemethod = (unsigned char)arg; + break; + case CURLOPT_FTP_USE_EPRT: + data->set.ftp_use_eprt = enabled; break; - case CURLOPT_HTTP09_ALLOWED: - arg = (long)va_arg(param, unsigned long); - if(arg > 1L) - return CURLE_BAD_FUNCTION_ARGUMENT; -#ifdef USE_HYPER - /* Hyper does not support HTTP/0.9 */ - if(arg) + case CURLOPT_FTP_USE_EPSV: + data->set.ftp_use_epsv = enabled; + break; + + case CURLOPT_FTP_USE_PRET: + data->set.ftp_use_pret = enabled; + break; + + case CURLOPT_FTP_SSL_CCC: + if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) return CURLE_BAD_FUNCTION_ARGUMENT; -#else - data->set.http09_allowed = !!arg; -#endif + data->set.ftp_ccc = (unsigned char)arg; break; - case CURLOPT_HTTP200ALIASES: + case CURLOPT_FTP_SKIP_PASV_IP: /* - * Set a list of aliases for HTTP 200 in response header + * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the + * bypass of the IP address in PASV responses. */ - data->set.http200aliases = va_arg(param, struct curl_slist *); + data->set.ftp_skip_ip = enabled; break; -#endif /* CURL_DISABLE_HTTP */ -#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ - !defined(CURL_DISABLE_IMAP) -# if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MIME) - case CURLOPT_HTTPHEADER: + case CURLOPT_FTPSSLAUTH: /* - * Set a list with HTTP headers to use (or replace internals with) + * Set a specific auth for FTP-SSL transfers. */ - data->set.headers = va_arg(param, struct curl_slist *); + if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ftpsslauth = (unsigned char)(curl_ftpauth)arg; break; -# endif - -# ifndef CURL_DISABLE_MIME - case CURLOPT_MIMEPOST: + case CURLOPT_ACCEPTTIMEOUT_MS: /* - * Set to make us do MIME POST + * The maximum time for curl to wait for FTP server connect */ - result = Curl_mime_set_subparts(&data->set.mimepost, - va_arg(param, curl_mime *), FALSE); - if(!result) { - data->set.method = HTTPREQ_POST_MIME; - data->set.opt_no_body = FALSE; /* this is implied */ -#ifndef CURL_DISABLE_FORM_API - Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); - data->state.mimepost = NULL; -#endif - } + if(uarg > UINT_MAX) + uarg = UINT_MAX; + data->set.accepttimeout = (unsigned int)uarg; break; - - case CURLOPT_MIME_OPTIONS: - arg = va_arg(param, long); - data->set.mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE); - break; -# endif -#endif - - case CURLOPT_HTTPAUTH: + case CURLOPT_WILDCARDMATCH: + data->set.wildcard_enabled = enabled; + break; +#endif /* ! CURL_DISABLE_FTP */ +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + case CURLOPT_FTP_CREATE_MISSING_DIRS: /* - * Set HTTP Authentication type BITMASK. + * An FTP/SFTP option that modifies an upload to create missing + * directories on the server. */ - { - int bitcheck; - bool authbits; - unsigned long auth = va_arg(param, unsigned long); - - if(auth == CURLAUTH_NONE) { - data->set.httpauth = auth; - break; - } - - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - data->state.authhost.iestyle = !!(auth & CURLAUTH_DIGEST_IE); - - if(auth & CURLAUTH_DIGEST_IE) { - auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ - } - - /* switch off bits we cannot support */ -#ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ -#endif -#ifndef USE_SPNEGO - auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without - GSS-API or SSPI */ -#endif - - /* check if any auth bit lower than CURLAUTH_ONLY is still set */ - bitcheck = 0; - authbits = FALSE; - while(bitcheck < 31) { - if(auth & (1UL << bitcheck++)) { - authbits = TRUE; - break; - } - } - if(!authbits) - return CURLE_NOT_BUILT_IN; /* no supported types left! */ - - data->set.httpauth = auth; - } - break; - - case CURLOPT_CUSTOMREQUEST: + /* reserve other values for future use */ + if((arg < CURLFTP_CREATE_DIR_NONE) || (arg > CURLFTP_CREATE_DIR_RETRY)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.ftp_create_missing_dirs = (unsigned char)arg; + break; +#endif /* ! CURL_DISABLE_FTP || USE_SSH */ + case CURLOPT_INFILESIZE: /* - * Set a custom string to use as request + * If known, this should inform curl about the file size of the + * to-be-uploaded file. */ - result = Curl_setstropt(&data->set.str[STRING_CUSTOMREQUEST], - va_arg(param, char *)); - - /* we do not set - data->set.method = HTTPREQ_CUSTOM; - here, we continue as if we were using the already set type - and this just changes the actual request keyword */ + if(arg < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.filesize = arg; break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_HTTPPROXYTUNNEL: + case CURLOPT_LOW_SPEED_LIMIT: /* - * Tunnel operations through the proxy instead of normal proxy use + * The low speed limit that if transfers are below this for + * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. */ - data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)); + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.low_speed_limit = arg; break; - - case CURLOPT_PROXYPORT: + case CURLOPT_LOW_SPEED_TIME: /* - * Explicitly set HTTP proxy port number. + * The low speed time that if transfers are below the set + * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted. + */ + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.low_speed_time = arg; + break; + case CURLOPT_PORT: + /* + * The port number to use when getting the URL. 0 disables it. */ - arg = va_arg(param, long); if((arg < 0) || (arg > 65535)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.proxyport = (unsigned short)arg; + data->set.use_port = (unsigned short)arg; break; - - case CURLOPT_PROXYAUTH: + case CURLOPT_TIMEOUT: /* - * Set HTTP Authentication type BITMASK. + * The maximum time you allow curl to use for a single transfer + * operation. */ - { - int bitcheck; - bool authbits; - unsigned long auth = va_arg(param, unsigned long); - - if(auth == CURLAUTH_NONE) { - data->set.proxyauth = auth; - break; - } - - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - data->state.authproxy.iestyle = !!(auth & CURLAUTH_DIGEST_IE); - - if(auth & CURLAUTH_DIGEST_IE) { - auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ - } - /* switch off bits we cannot support */ -#ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ -#endif -#ifndef USE_SPNEGO - auth &= ~CURLAUTH_NEGOTIATE; /* no Negotiate (SPNEGO) auth without - GSS-API or SSPI */ -#endif - - /* check if any auth bit lower than CURLAUTH_ONLY is still set */ - bitcheck = 0; - authbits = FALSE; - while(bitcheck < 31) { - if(auth & (1UL << bitcheck++)) { - authbits = TRUE; - break; - } - } - if(!authbits) - return CURLE_NOT_BUILT_IN; /* no supported types left! */ + if((arg >= 0) && (arg <= (INT_MAX/1000))) + data->set.timeout = (unsigned int)arg * 1000; + else + return CURLE_BAD_FUNCTION_ARGUMENT; + break; - data->set.proxyauth = auth; - } - break; + case CURLOPT_TIMEOUT_MS: + if(uarg > UINT_MAX) + uarg = UINT_MAX; + data->set.timeout = (unsigned int)uarg; + break; - case CURLOPT_PROXY: + case CURLOPT_CONNECTTIMEOUT: /* - * Set proxy server:port to use as proxy. - * - * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) - * we explicitly say that we do not want to use a proxy - * (even though there might be environment variables saying so). - * - * Setting it to NULL, means no proxy but allows the environment variables - * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). + * The maximum time you allow curl to use to connect. */ - result = Curl_setstropt(&data->set.str[STRING_PROXY], - va_arg(param, char *)); + if((arg >= 0) && (arg <= (INT_MAX/1000))) + data->set.connecttimeout = (unsigned int)arg * 1000; + else + return CURLE_BAD_FUNCTION_ARGUMENT; break; - case CURLOPT_PRE_PROXY: - /* - * Set proxy server:port to use as SOCKS proxy. - * - * If the proxy is set to "" or NULL we explicitly say that we do not want - * to use the socks proxy. - */ - result = Curl_setstropt(&data->set.str[STRING_PRE_PROXY], - va_arg(param, char *)); + case CURLOPT_CONNECTTIMEOUT_MS: + if(uarg > UINT_MAX) + uarg = UINT_MAX; + data->set.connecttimeout = (unsigned int)uarg; break; - case CURLOPT_PROXYTYPE: + case CURLOPT_RESUME_FROM: /* - * Set proxy type. + * Resume transfer at the given file position */ - arg = va_arg(param, long); - if((arg < CURLPROXY_HTTP) || (arg > CURLPROXY_SOCKS5_HOSTNAME)) + if(arg < -1) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.proxytype = (unsigned char)(curl_proxytype)arg; + data->set.set_resume_from = arg; break; - case CURLOPT_PROXY_TRANSFER_MODE: + case CURLOPT_CRLF: /* - * set transfer mode (;type=) when doing FTP via an HTTP proxy + * Kludgy option to enable CRLF conversions. Subject for removal. */ - switch(va_arg(param, long)) { - case 0: - data->set.proxy_transfer_mode = FALSE; - break; - case 1: - data->set.proxy_transfer_mode = TRUE; - break; - default: - /* reserve other values for future use */ - result = CURLE_BAD_FUNCTION_ARGUMENT; - break; - } - break; - - case CURLOPT_SOCKS5_AUTH: - data->set.socks5auth = (unsigned char)va_arg(param, unsigned long); - if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI)) - result = CURLE_NOT_BUILT_IN; + data->set.crlf = enabled; break; -#endif /* CURL_DISABLE_PROXY */ -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - case CURLOPT_SOCKS5_GSSAPI_NEC: +#ifndef CURL_DISABLE_BINDLOCAL + case CURLOPT_LOCALPORT: /* - * Set flag for NEC SOCK5 support + * Set what local port to bind the socket to when performing an operation. */ - data->set.socks5_gssapi_nec = (0 != va_arg(param, long)); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.localport = curlx_sltous(arg); break; -#endif -#ifndef CURL_DISABLE_PROXY - case CURLOPT_SOCKS5_GSSAPI_SERVICE: - case CURLOPT_PROXY_SERVICE_NAME: + case CURLOPT_LOCALPORTRANGE: /* - * Set proxy authentication service name for Kerberos 5 and SPNEGO + * Set number of local ports to try, starting with CURLOPT_LOCALPORT. */ - result = Curl_setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME], - va_arg(param, char *)); + if((arg < 0) || (arg > 65535)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.localportrange = curlx_sltous(arg); break; #endif - case CURLOPT_SERVICE_NAME: - /* - * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO - */ - result = Curl_setstropt(&data->set.str[STRING_SERVICE_NAME], - va_arg(param, char *)); - break; - case CURLOPT_HEADERDATA: + case CURLOPT_GSSAPI_DELEGATION: /* - * Custom pointer to pass the header write callback function + * GSS-API credential delegation bitmask */ - data->set.writeheader = (void *)va_arg(param, void *); + data->set.gssapi_delegation = (unsigned char)uarg& + (CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG); break; - case CURLOPT_ERRORBUFFER: + case CURLOPT_SSL_VERIFYPEER: /* - * Error buffer provided by the caller to get the human readable - * error string in. + * Enable peer SSL verifying. */ - data->set.errorbuffer = va_arg(param, char *); + data->set.ssl.primary.verifypeer = enabled; + + /* Update the current connection ssl_config. */ + Curl_ssl_conn_config_update(data, FALSE); break; - case CURLOPT_WRITEDATA: +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_SSL_VERIFYPEER: /* - * FILE pointer to write to. Or possibly - * used as argument to the write callback. + * Enable peer SSL verifying for DoH. */ - data->set.out = va_arg(param, void *); + data->set.doh_verifypeer = enabled; break; - -#ifdef CURL_LIST_ONLY_PROTOCOL - case CURLOPT_DIRLISTONLY: + case CURLOPT_DOH_SSL_VERIFYHOST: /* - * An option that changes the command to one that asks for a list only, no - * file info details. Used for FTP, POP3 and SFTP. + * Enable verification of the hostname in the peer certificate for DoH */ - data->set.list_only = (0 != va_arg(param, long)); + data->set.doh_verifyhost = enabled; break; -#endif - case CURLOPT_APPEND: + case CURLOPT_DOH_SSL_VERIFYSTATUS: /* - * We want to upload and append to an existing file. Used for FTP and - * SFTP. + * Enable certificate status verifying for DoH. */ - data->set.remote_append = (0 != va_arg(param, long)); - break; + if(!Curl_ssl_cert_status_request()) + return CURLE_NOT_BUILT_IN; -#ifndef CURL_DISABLE_FTP - case CURLOPT_FTP_FILEMETHOD: - /* - * How do access files over FTP. - */ - arg = va_arg(param, long); - if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_filemethod = (unsigned char)arg; + data->set.doh_verifystatus = enabled; break; - case CURLOPT_FTPPORT: +#endif /* ! CURL_DISABLE_DOH */ + case CURLOPT_SSL_VERIFYHOST: /* - * Use FTP PORT, this also specifies which IP address to use + * Enable verification of the hostname in the peer certificate */ - result = Curl_setstropt(&data->set.str[STRING_FTPPORT], - va_arg(param, char *)); - data->set.ftp_use_port = !!(data->set.str[STRING_FTPPORT]); - break; - case CURLOPT_FTP_USE_EPRT: - data->set.ftp_use_eprt = (0 != va_arg(param, long)); - break; + /* Obviously people are not reading documentation and too many thought + this argument took a boolean when it was not and misused it. + Treat 1 and 2 the same */ + data->set.ssl.primary.verifyhost = enabled; - case CURLOPT_FTP_USE_EPSV: - data->set.ftp_use_epsv = (0 != va_arg(param, long)); + /* Update the current connection ssl_config. */ + Curl_ssl_conn_config_update(data, FALSE); break; + case CURLOPT_SSL_VERIFYSTATUS: + /* + * Enable certificate status verifying. + */ + if(!Curl_ssl_cert_status_request()) + return CURLE_NOT_BUILT_IN; - case CURLOPT_FTP_USE_PRET: - data->set.ftp_use_pret = (0 != va_arg(param, long)); - break; + data->set.ssl.primary.verifystatus = enabled; - case CURLOPT_FTP_SSL_CCC: - arg = va_arg(param, long); - if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftp_ccc = (unsigned char)arg; + /* Update the current connection ssl_config. */ + Curl_ssl_conn_config_update(data, FALSE); break; - - case CURLOPT_FTP_SKIP_PASV_IP: + case CURLOPT_SSL_FALSESTART: /* - * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the - * bypass of the IP address in PASV responses. + * Enable TLS false start. */ - data->set.ftp_skip_ip = (0 != va_arg(param, long)); - break; + if(!Curl_ssl_false_start(data)) + return CURLE_NOT_BUILT_IN; - case CURLOPT_FTP_ACCOUNT: - result = Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT], - va_arg(param, char *)); + data->set.ssl.falsestart = enabled; + break; + case CURLOPT_CERTINFO: +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CERTINFO)) + data->set.ssl.certinfo = enabled; + else +#endif + return CURLE_NOT_BUILT_IN; break; + case CURLOPT_BUFFERSIZE: + /* + * The application kindly asks for a differently sized receive buffer. + * If it seems reasonable, we will use it. + */ + if(arg > READBUFFER_MAX) + arg = READBUFFER_MAX; + else if(arg < 1) + arg = READBUFFER_SIZE; + else if(arg < READBUFFER_MIN) + arg = READBUFFER_MIN; - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - result = Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER], - va_arg(param, char *)); + data->set.buffer_size = (unsigned int)arg; break; - case CURLOPT_FTPSSLAUTH: + case CURLOPT_UPLOAD_BUFFERSIZE: /* - * Set a specific auth for FTP-SSL transfers. + * The application kindly asks for a differently sized upload buffer. + * Cap it to sensible. */ - arg = va_arg(param, long); - if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ftpsslauth = (unsigned char)(curl_ftpauth)arg; + if(arg > UPLOADBUFFER_MAX) + arg = UPLOADBUFFER_MAX; + else if(arg < UPLOADBUFFER_MIN) + arg = UPLOADBUFFER_MIN; + + data->set.upload_buffer_size = (unsigned int)arg; break; -#ifdef HAVE_GSSAPI - case CURLOPT_KRBLEVEL: + + case CURLOPT_NOSIGNAL: /* - * A string that defines the kerberos security level. + * The application asks not to set any signal() or alarm() handlers, + * even when using a timeout. */ - result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL], - va_arg(param, char *)); - data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]); + data->set.no_signal = enabled; break; -#endif -#endif -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) - case CURLOPT_FTP_CREATE_MISSING_DIRS: + case CURLOPT_MAXFILESIZE: /* - * An FTP/SFTP option that modifies an upload to create missing - * directories on the server. + * Set the maximum size of a file to download. */ - arg = va_arg(param, long); - /* reserve other values for future use */ - if((arg < CURLFTP_CREATE_DIR_NONE) || - (arg > CURLFTP_CREATE_DIR_RETRY)) - result = CURLE_BAD_FUNCTION_ARGUMENT; - else - data->set.ftp_create_missing_dirs = (unsigned char)arg; + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_filesize = arg; break; - case CURLOPT_POSTQUOTE: +#ifdef USE_SSL + case CURLOPT_USE_SSL: /* - * List of RAW FTP commands to use after a transfer + * Make transfers attempt to use SSL/TLS. */ - data->set.postquote = va_arg(param, struct curl_slist *); + if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.use_ssl = (unsigned char)arg; break; - case CURLOPT_PREQUOTE: - /* - * List of RAW FTP commands to use prior to RETR (Wesley Laxton) - */ - data->set.prequote = va_arg(param, struct curl_slist *); + case CURLOPT_SSL_OPTIONS: + data->set.ssl.primary.ssl_options = (unsigned char)(arg & 0xff); + data->set.ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); + data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); + data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); + data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); + data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); + data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); + data->set.ssl.earlydata = !!(arg & CURLSSLOPT_EARLYDATA); + /* If a setting is added here it should also be added in dohprobe() + which sets its own CURLOPT_SSL_OPTIONS based on these settings. */ break; - case CURLOPT_QUOTE: - /* - * List of RAW FTP commands to use before a transfer - */ - data->set.quote = va_arg(param, struct curl_slist *); + +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSL_OPTIONS: + data->set.proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff); + data->set.proxy_ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); + data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); + data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); + data->set.proxy_ssl.revoke_best_effort = + !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); + data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); + data->set.proxy_ssl.auto_client_cert = + !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); break; #endif - case CURLOPT_READDATA: - /* - * FILE pointer to read the file to be uploaded from. Or possibly - * used as argument to the read callback. - */ - data->set.in_set = va_arg(param, void *); - break; - case CURLOPT_INFILESIZE: - /* - * If known, this should inform curl about the file size of the - * to-be-uploaded file. - */ - arg = va_arg(param, long); - if(arg < -1) + +#endif /* USE_SSL */ + case CURLOPT_IPRESOLVE: + if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.filesize = arg; + data->set.ipver = (unsigned char) arg; break; - case CURLOPT_INFILESIZE_LARGE: + case CURLOPT_TCP_NODELAY: /* - * If known, this should inform curl about the file size of the - * to-be-uploaded file. + * Enable or disable TCP_NODELAY, which will disable/enable the Nagle + * algorithm */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.filesize = bigsize; + data->set.tcp_nodelay = enabled; break; - case CURLOPT_LOW_SPEED_LIMIT: - /* - * The low speed limit that if transfers are below this for - * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. - */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.low_speed_limit = arg; + + case CURLOPT_IGNORE_CONTENT_LENGTH: + data->set.ignorecl = enabled; break; - case CURLOPT_MAX_SEND_SPEED_LARGE: + + case CURLOPT_CONNECT_ONLY: /* - * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE - * bytes per second the transfer is throttled.. + * No data transfer. + * (1) - only do connection + * (2) - do first get request but get no content */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < 0) + if(arg > 2) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.max_send_speed = bigsize; + data->set.connect_only = (unsigned char)arg; break; - case CURLOPT_MAX_RECV_SPEED_LARGE: - /* - * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per - * second the transfer is throttled.. - */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.max_recv_speed = bigsize; + + case CURLOPT_SSL_SESSIONID_CACHE: + data->set.ssl.primary.cache_session = enabled; +#ifndef CURL_DISABLE_PROXY + data->set.proxy_ssl.primary.cache_session = + data->set.ssl.primary.cache_session; +#endif break; - case CURLOPT_LOW_SPEED_TIME: + +#ifdef USE_SSH + /* we only include SSH options if explicitly built to support SSH */ + case CURLOPT_SSH_AUTH_TYPES: + data->set.ssh_auth_types = (int)arg; + break; + case CURLOPT_SSH_COMPRESSION: + data->set.ssh_compression = enabled; + break; +#endif + + case CURLOPT_HTTP_TRANSFER_DECODING: /* - * The low speed time that if transfers are below the set - * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted. + * disable libcurl transfer encoding is used */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.low_speed_time = arg; +#ifndef USE_HYPER + data->set.http_te_skip = !enabled; /* reversed */ break; - case CURLOPT_CURLU: +#else + return CURLE_NOT_BUILT_IN; /* hyper does not support */ +#endif + + case CURLOPT_HTTP_CONTENT_DECODING: /* - * pass CURLU to set URL + * raw data passed to the application when content encoding is used */ - data->set.uh = va_arg(param, CURLU *); + data->set.http_ce_skip = !enabled; /* reversed */ break; - case CURLOPT_URL: + +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + case CURLOPT_NEW_FILE_PERMS: /* - * The URL to fetch. + * Uses these permissions instead of 0644 */ - if(data->state.url_alloc) { - /* the already set URL is allocated, free it first! */ - Curl_safefree(data->state.url); - data->state.url_alloc = FALSE; - } - result = Curl_setstropt(&data->set.str[STRING_SET_URL], - va_arg(param, char *)); - data->state.url = data->set.str[STRING_SET_URL]; + if((arg < 0) || (arg > 0777)) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.new_file_perms = (unsigned int)arg; break; - case CURLOPT_PORT: +#endif +#ifdef USE_SSH + case CURLOPT_NEW_DIRECTORY_PERMS: /* - * The port number to use when getting the URL. 0 disables it. + * Uses these permissions instead of 0755 */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) + if((arg < 0) || (arg > 0777)) return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.use_port = (unsigned short)arg; + data->set.new_directory_perms = (unsigned int)arg; break; - case CURLOPT_TIMEOUT: +#endif +#ifdef USE_IPV6 + case CURLOPT_ADDRESS_SCOPE: /* - * The maximum time you allow curl to use for a single transfer - * operation. + * Use this scope id when using IPv6 + * We always get longs when passed plain numericals so we should check + * that the value fits into an unsigned 32-bit integer. */ - arg = va_arg(param, long); - if((arg >= 0) && (arg <= (INT_MAX/1000))) - data->set.timeout = (unsigned int)arg * 1000; - else +#if SIZEOF_LONG > 4 + if(uarg > UINT_MAX) return CURLE_BAD_FUNCTION_ARGUMENT; +#endif + data->set.scope_id = (unsigned int)uarg; break; - - case CURLOPT_TIMEOUT_MS: - uarg = va_arg(param, unsigned long); - if(uarg > UINT_MAX) - uarg = UINT_MAX; - data->set.timeout = (unsigned int)uarg; +#endif + case CURLOPT_PROTOCOLS: + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal with. + Defaults to CURLPROTO_ALL. */ + data->set.allowed_protocols = (curl_prot_t)arg; break; - case CURLOPT_CONNECTTIMEOUT: + case CURLOPT_REDIR_PROTOCOLS: + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol + needs to be set in both bitmasks to be allowed to get redirected to. */ + data->set.redir_protocols = (curl_prot_t)arg; + break; + +#ifndef CURL_DISABLE_SMTP + case CURLOPT_MAIL_RCPT_ALLOWFAILS: + /* allow RCPT TO command to fail for some recipients */ + data->set.mail_rcpt_allowfails = enabled; + break; +#endif /* !CURL_DISABLE_SMTP */ + case CURLOPT_SASL_IR: + /* Enable/disable SASL initial response */ + data->set.sasl_ir = enabled; + break; +#ifndef CURL_DISABLE_RTSP + case CURLOPT_RTSP_REQUEST: + { /* - * The maximum time you allow curl to use to connect. + * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) + * Would this be better if the RTSPREQ_* were just moved into here? */ - arg = va_arg(param, long); - if((arg >= 0) && (arg <= (INT_MAX/1000))) - data->set.connecttimeout = (unsigned int)arg * 1000; - else + Curl_RtspReq rtspreq = RTSPREQ_NONE; + switch(arg) { + case CURL_RTSPREQ_OPTIONS: + rtspreq = RTSPREQ_OPTIONS; + break; + + case CURL_RTSPREQ_DESCRIBE: + rtspreq = RTSPREQ_DESCRIBE; + break; + + case CURL_RTSPREQ_ANNOUNCE: + rtspreq = RTSPREQ_ANNOUNCE; + break; + + case CURL_RTSPREQ_SETUP: + rtspreq = RTSPREQ_SETUP; + break; + + case CURL_RTSPREQ_PLAY: + rtspreq = RTSPREQ_PLAY; + break; + + case CURL_RTSPREQ_PAUSE: + rtspreq = RTSPREQ_PAUSE; + break; + + case CURL_RTSPREQ_TEARDOWN: + rtspreq = RTSPREQ_TEARDOWN; + break; + + case CURL_RTSPREQ_GET_PARAMETER: + rtspreq = RTSPREQ_GET_PARAMETER; + break; + + case CURL_RTSPREQ_SET_PARAMETER: + rtspreq = RTSPREQ_SET_PARAMETER; + break; + + case CURL_RTSPREQ_RECORD: + rtspreq = RTSPREQ_RECORD; + break; + + case CURL_RTSPREQ_RECEIVE: + rtspreq = RTSPREQ_RECEIVE; + break; + default: return CURLE_BAD_FUNCTION_ARGUMENT; - break; + } - case CURLOPT_CONNECTTIMEOUT_MS: - uarg = va_arg(param, unsigned long); - if(uarg > UINT_MAX) - uarg = UINT_MAX; - data->set.connecttimeout = (unsigned int)uarg; + data->set.rtspreq = rtspreq; break; - -#ifndef CURL_DISABLE_FTP - case CURLOPT_ACCEPTTIMEOUT_MS: + } + case CURLOPT_RTSP_CLIENT_CSEQ: /* - * The maximum time for curl to wait for FTP server connect + * Set the CSEQ number to issue for the next RTSP request. Useful if the + * application is resuming a previously broken connection. The CSEQ + * will increment from this new number henceforth. */ - uarg = va_arg(param, unsigned long); + data->state.rtsp_next_client_CSeq = arg; + break; + + case CURLOPT_RTSP_SERVER_CSEQ: + /* Same as the above, but for server-initiated requests */ + data->state.rtsp_next_server_CSeq = arg; + break; + +#endif /* ! CURL_DISABLE_RTSP */ + + case CURLOPT_TCP_KEEPALIVE: + data->set.tcp_keepalive = enabled; + break; + case CURLOPT_TCP_KEEPIDLE: + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + data->set.tcp_keepidle = (int)arg; + break; + case CURLOPT_TCP_KEEPINTVL: + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + data->set.tcp_keepintvl = (int)arg; + break; + case CURLOPT_TCP_KEEPCNT: + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if(arg > INT_MAX) + arg = INT_MAX; + data->set.tcp_keepcnt = (int)arg; + break; + case CURLOPT_TCP_FASTOPEN: +#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \ + defined(TCP_FASTOPEN_CONNECT) + data->set.tcp_fastopen = enabled; +#else + return CURLE_NOT_BUILT_IN; +#endif + break; + case CURLOPT_SSL_ENABLE_NPN: + break; + case CURLOPT_SSL_ENABLE_ALPN: + data->set.ssl_enable_alpn = enabled; + break; + case CURLOPT_PATH_AS_IS: + data->set.path_as_is = enabled; + break; + case CURLOPT_PIPEWAIT: + data->set.pipewait = enabled; + break; + case CURLOPT_STREAM_WEIGHT: +#if defined(USE_HTTP2) || defined(USE_HTTP3) + if((arg >= 1) && (arg <= 256)) + data->set.priority.weight = (int)arg; + break; +#else + return CURLE_NOT_BUILT_IN; +#endif + case CURLOPT_SUPPRESS_CONNECT_HEADERS: + data->set.suppress_connect_headers = enabled; + break; + case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: if(uarg > UINT_MAX) uarg = UINT_MAX; - data->set.accepttimeout = (unsigned int)uarg; + data->set.happy_eyeballs_timeout = (unsigned int)uarg; + break; +#ifndef CURL_DISABLE_SHUFFLE_DNS + case CURLOPT_DNS_SHUFFLE_ADDRESSES: + data->set.dns_shuffle_addresses = enabled; break; #endif + case CURLOPT_DISALLOW_USERNAME_IN_URL: + data->set.disallow_username_in_url = enabled; + break; - case CURLOPT_USERPWD: + case CURLOPT_UPKEEP_INTERVAL_MS: + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.upkeep_interval_ms = arg; + break; + case CURLOPT_MAXAGE_CONN: + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxage_conn = arg; + break; + case CURLOPT_MAXLIFETIME_CONN: + if(arg < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.maxlifetime_conn = arg; + break; +#ifndef CURL_DISABLE_HSTS + case CURLOPT_HSTS_CTRL: + if(arg & CURLHSTS_ENABLE) { + if(!data->hsts) { + data->hsts = Curl_hsts_init(); + if(!data->hsts) + return CURLE_OUT_OF_MEMORY; + } + } + else + Curl_hsts_cleanup(&data->hsts); + break; +#endif /* ! CURL_DISABLE_HSTS */ +#ifndef CURL_DISABLE_ALTSVC + case CURLOPT_ALTSVC_CTRL: + if(!arg) { + DEBUGF(infof(data, "bad CURLOPT_ALTSVC_CTRL input")); + return CURLE_BAD_FUNCTION_ARGUMENT; + } + if(!data->asi) { + data->asi = Curl_altsvc_init(); + if(!data->asi) + return CURLE_OUT_OF_MEMORY; + } + return Curl_altsvc_ctrl(data->asi, arg); +#endif /* ! CURL_DISABLE_ALTSVC */ +#ifndef CURL_DISABLE_WEBSOCKETS + case CURLOPT_WS_OPTIONS: + data->set.ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE); + break; +#endif + case CURLOPT_QUICK_EXIT: + data->set.quick_exit = enabled; + break; + case CURLOPT_DNS_USE_GLOBAL_CACHE: + /* deprecated */ + break; + case CURLOPT_SSLENGINE_DEFAULT: /* - * user:password to use in the operation + * flag to set engine as default. */ - result = setstropt_userpwd(va_arg(param, char *), - &data->set.str[STRING_USERNAME], - &data->set.str[STRING_PASSWORD]); - break; + Curl_safefree(data->set.str[STRING_SSL_ENGINE]); + return Curl_ssl_set_engine_default(data); - case CURLOPT_USERNAME: + default: + /* unknown option */ + return CURLE_UNKNOWN_OPTION; + } + return CURLE_OK; +} + +static CURLcode setopt_slist(struct Curl_easy *data, CURLoption option, + struct curl_slist *slist) +{ + CURLcode result = CURLE_OK; + switch(option) { +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXYHEADER: /* - * authentication username to use in the operation + * Set a list with proxy headers to use (or replace internals with) + * + * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a + * long time we remain doing it this way until CURLOPT_PROXYHEADER is + * used. As soon as this option has been used, if set to anything but + * NULL, custom headers for proxies are only picked from this list. + * + * Set this option to NULL to restore the previous behavior. */ - result = Curl_setstropt(&data->set.str[STRING_USERNAME], - va_arg(param, char *)); + data->set.proxyheaders = slist; break; - case CURLOPT_PASSWORD: +#endif +#ifndef CURL_DISABLE_HTTP + case CURLOPT_HTTP200ALIASES: /* - * authentication password to use in the operation + * Set a list of aliases for HTTP 200 in response header */ - result = Curl_setstropt(&data->set.str[STRING_PASSWORD], - va_arg(param, char *)); + data->set.http200aliases = slist; break; - - case CURLOPT_LOGIN_OPTIONS: +#endif +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) + case CURLOPT_POSTQUOTE: /* - * authentication options to use in the operation + * List of RAW FTP commands to use after a transfer */ - result = Curl_setstropt(&data->set.str[STRING_OPTIONS], - va_arg(param, char *)); + data->set.postquote = slist; break; - - case CURLOPT_XOAUTH2_BEARER: + case CURLOPT_PREQUOTE: /* - * OAuth 2.0 bearer token to use in the operation + * List of RAW FTP commands to use prior to RETR (Wesley Laxton) */ - result = Curl_setstropt(&data->set.str[STRING_BEARER], - va_arg(param, char *)); + data->set.prequote = slist; break; - + case CURLOPT_QUOTE: + /* + * List of RAW FTP commands to use before a transfer + */ + data->set.quote = slist; + break; +#endif case CURLOPT_RESOLVE: /* * List of HOST:PORT:[addresses] strings to populate the DNS cache with @@ -1587,123 +1479,83 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * This API can remove any entry from the DNS cache, but only entries * that are not actually in use right now will be pruned immediately. */ - data->set.resolve = va_arg(param, struct curl_slist *); + data->set.resolve = slist; data->state.resolve = data->set.resolve; break; - case CURLOPT_PROGRESSFUNCTION: +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MIME) + case CURLOPT_HTTPHEADER: /* - * Progress callback function + * Set a list with HTTP headers to use (or replace internals with) */ - data->set.fprogress = va_arg(param, curl_progress_callback); - if(data->set.fprogress) - data->progress.callback = TRUE; /* no longer internal */ - else - data->progress.callback = FALSE; /* NULL enforces internal */ + data->set.headers = slist; break; - - case CURLOPT_XFERINFOFUNCTION: +#endif +#ifndef CURL_DISABLE_TELNET + case CURLOPT_TELNETOPTIONS: /* - * Transfer info callback function - */ - data->set.fxferinfo = va_arg(param, curl_xferinfo_callback); - if(data->set.fxferinfo) - data->progress.callback = TRUE; /* no longer internal */ - else - data->progress.callback = FALSE; /* NULL enforces internal */ - - break; - - case CURLOPT_PROGRESSDATA: - /* - * Custom client data to pass to the progress callback - */ - data->set.progress_client = va_arg(param, void *); - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYUSERPWD: { - /* - * user:password needed to use the proxy - */ - char *u = NULL; - char *p = NULL; - result = setstropt_userpwd(va_arg(param, char *), &u, &p); - - /* URL decode the components */ - if(!result && u) - result = Curl_urldecode(u, 0, &data->set.str[STRING_PROXYUSERNAME], NULL, - REJECT_ZERO); - if(!result && p) - result = Curl_urldecode(p, 0, &data->set.str[STRING_PROXYPASSWORD], NULL, - REJECT_ZERO); - free(u); - free(p); - } - break; - case CURLOPT_PROXYUSERNAME: - /* - * authentication username to use in the operation - */ - result = Curl_setstropt(&data->set.str[STRING_PROXYUSERNAME], - va_arg(param, char *)); - break; - case CURLOPT_PROXYPASSWORD: - /* - * authentication password to use in the operation - */ - result = Curl_setstropt(&data->set.str[STRING_PROXYPASSWORD], - va_arg(param, char *)); - break; - case CURLOPT_NOPROXY: - /* - * proxy exception list + * Set a linked list of telnet options */ - result = Curl_setstropt(&data->set.str[STRING_NOPROXY], - va_arg(param, char *)); + data->set.telnet_options = slist; break; #endif - - case CURLOPT_RANGE: - /* - * What range of the file you want to transfer - */ - result = Curl_setstropt(&data->set.str[STRING_SET_RANGE], - va_arg(param, char *)); - break; - case CURLOPT_RESUME_FROM: - /* - * Resume transfer at the given file position - */ - arg = va_arg(param, long); - if(arg < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.set_resume_from = arg; +#ifndef CURL_DISABLE_SMTP + case CURLOPT_MAIL_RCPT: + /* Set the list of mail recipients */ + data->set.mail_rcpt = slist; break; - case CURLOPT_RESUME_FROM_LARGE: - /* - * Resume transfer at the given file position - */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < -1) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.set_resume_from = bigsize; +#endif + case CURLOPT_CONNECT_TO: + data->set.connect_to = slist; break; - case CURLOPT_DEBUGFUNCTION: - /* - * stderr write callback. - */ - data->set.fdebug = va_arg(param, curl_debug_callback); + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} + +/* assorted pointer type arguments */ +static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option, + va_list param) +{ + CURLcode result = CURLE_OK; + switch(option) { +#ifndef CURL_DISABLE_HTTP +#ifndef CURL_DISABLE_FORM_API + case CURLOPT_HTTPPOST: /* - * if the callback provided is NULL, it will use the default callback + * Set to make us do HTTP POST. Legacy API-style. */ + data->set.httppost = va_arg(param, struct curl_httppost *); + data->set.method = HTTPREQ_POST_FORM; + data->set.opt_no_body = FALSE; /* this is implied */ + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); + data->state.mimepost = NULL; break; - case CURLOPT_DEBUGDATA: +#endif /* ! CURL_DISABLE_FORM_API */ +#endif /* ! CURL_DISABLE_HTTP */ +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \ + !defined(CURL_DISABLE_IMAP) +# ifndef CURL_DISABLE_MIME + case CURLOPT_MIMEPOST: /* - * Set to a void * that should receive all error writes. This - * defaults to CURLOPT_STDERR for normal operations. + * Set to make us do MIME POST */ - data->set.debugdata = va_arg(param, void *); + result = Curl_mime_set_subparts(&data->set.mimepost, + va_arg(param, curl_mime *), + FALSE); + if(!result) { + data->set.method = HTTPREQ_POST_MIME; + data->set.opt_no_body = FALSE; /* this is implied */ +#ifndef CURL_DISABLE_FORM_API + Curl_mime_cleanpart(data->state.formp); + Curl_safefree(data->state.formp); + data->state.mimepost = NULL; +#endif + } break; +#endif /* ! CURL_DISABLE_MIME */ +#endif /* ! disabled HTTP, SMTP or IMAP */ case CURLOPT_STDERR: /* * Set to a FILE * that should receive all error writes. This @@ -1713,877 +1565,883 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) if(!data->set.err) data->set.err = stderr; break; - case CURLOPT_HEADERFUNCTION: - /* - * Set header write callback - */ - data->set.fwrite_header = va_arg(param, curl_write_callback); - break; - case CURLOPT_WRITEFUNCTION: - /* - * Set data write callback - */ - data->set.fwrite_func = va_arg(param, curl_write_callback); - if(!data->set.fwrite_func) - /* When set to NULL, reset to our internal default function */ - data->set.fwrite_func = (curl_write_callback)fwrite; - break; - case CURLOPT_READFUNCTION: - /* - * Read data callback - */ - data->set.fread_func_set = va_arg(param, curl_read_callback); - if(!data->set.fread_func_set) { - data->set.is_fread_set = 0; - /* When set to NULL, reset to our internal default function */ - data->set.fread_func_set = (curl_read_callback)fread; + case CURLOPT_SHARE: + { + struct Curl_share *set = va_arg(param, struct Curl_share *); + + /* disconnect from old share, if any */ + if(data->share) { + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + + if(data->dns.hostcachetype == HCACHE_SHARED) { + data->dns.hostcache = NULL; + data->dns.hostcachetype = HCACHE_NONE; + } + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(data->share->cookies == data->cookies) + data->cookies = NULL; +#endif + +#ifndef CURL_DISABLE_HSTS + if(data->share->hsts == data->hsts) + data->hsts = NULL; +#endif +#ifdef USE_SSL + if(data->share->sslsession == data->state.session) + data->state.session = NULL; +#endif +#ifdef USE_LIBPSL + if(data->psl == &data->share->psl) + data->psl = data->multi ? &data->multi->psl : NULL; +#endif + + data->share->dirty--; + + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); + data->share = NULL; } - else - data->set.is_fread_set = 1; - break; - case CURLOPT_SEEKFUNCTION: - /* - * Seek callback. Might be NULL. - */ - data->set.seek_func = va_arg(param, curl_seek_callback); - break; - case CURLOPT_SEEKDATA: - /* - * Seek control callback. Might be NULL. - */ - data->set.seek_client = va_arg(param, void *); - break; - case CURLOPT_IOCTLFUNCTION: - /* - * I/O control callback. Might be NULL. - */ - data->set.ioctl_func = va_arg(param, curl_ioctl_callback); - break; - case CURLOPT_IOCTLDATA: - /* - * I/O control data pointer. Might be NULL. - */ - data->set.ioctl_client = va_arg(param, void *); - break; - case CURLOPT_SSLCERT: - /* - * String that holds filename of the SSL certificate to use - */ - result = Curl_setstropt(&data->set.str[STRING_CERT], - va_arg(param, char *)); - break; - case CURLOPT_SSLCERT_BLOB: - /* - * Blob that holds file content of the SSL certificate to use - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_CERT], - va_arg(param, struct curl_blob *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLCERT: - /* - * String that holds filename of the SSL certificate to use for proxy - */ - result = Curl_setstropt(&data->set.str[STRING_CERT_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSLCERT_BLOB: - /* - * Blob that holds file content of the SSL certificate to use for proxy - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY], - va_arg(param, struct curl_blob *)); - break; + + if(GOOD_SHARE_HANDLE(set)) + /* use new share if it set */ + data->share = set; + if(data->share) { + + Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + + data->share->dirty++; + + if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) { + /* use shared host cache */ + data->dns.hostcache = &data->share->hostcache; + data->dns.hostcachetype = HCACHE_SHARED; + } +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(data->share->cookies) { + /* use shared cookie list, first free own one if any */ + Curl_cookie_cleanup(data->cookies); + /* enable cookies since we now use a share that uses cookies! */ + data->cookies = data->share->cookies; + } +#endif /* CURL_DISABLE_HTTP */ +#ifndef CURL_DISABLE_HSTS + if(data->share->hsts) { + /* first free the private one if any */ + Curl_hsts_cleanup(&data->hsts); + data->hsts = data->share->hsts; + } #endif - case CURLOPT_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use - */ - result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE], - va_arg(param, char *)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use for proxy - */ - result = Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY], - va_arg(param, char *)); - break; +#ifdef USE_SSL + if(data->share->sslsession) { + data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; + data->state.session = data->share->sslsession; + } #endif - case CURLOPT_SSLKEY: - /* - * String that holds filename of the SSL key to use - */ - result = Curl_setstropt(&data->set.str[STRING_KEY], - va_arg(param, char *)); +#ifdef USE_LIBPSL + if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) + data->psl = &data->share->psl; +#endif + + Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); + } + /* check for host cache not needed, + * it will be done by curl_easy_perform */ + } + break; + +#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); break; - case CURLOPT_SSLKEY_BLOB: - /* - * Blob that holds file content of the SSL key to use - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_KEY], - va_arg(param, struct curl_blob *)); + } +#endif + + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} + +static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, + char *ptr) +{ + CURLcode result = CURLE_OK; + switch(option) { + case CURLOPT_SSL_CIPHER_LIST: + if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) + /* set a list of cipher we want to use in the SSL connection */ + return Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], ptr); + return CURLE_NOT_BUILT_IN; break; #ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLKEY: - /* - * String that holds filename of the SSL key to use for proxy - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_PROXY], - va_arg(param, char *)); - break; - case CURLOPT_PROXY_SSLKEY_BLOB: - /* - * Blob that holds file content of the SSL key to use for proxy - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY], - va_arg(param, struct curl_blob *)); + case CURLOPT_PROXY_SSL_CIPHER_LIST: + if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) { + /* set a list of cipher we want to use in the SSL connection for proxy */ + return Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST_PROXY], + ptr); + } + else + return CURLE_NOT_BUILT_IN; break; #endif - case CURLOPT_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE], - va_arg(param, char *)); + case CURLOPT_TLS13_CIPHERS: + if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) { + /* set preferred list of TLS 1.3 cipher suites */ + return Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST], ptr); + } + else + return CURLE_NOT_BUILT_IN; break; #ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use for proxy - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_TYPE_PROXY], - va_arg(param, char *)); + case CURLOPT_PROXY_TLS13_CIPHERS: + if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) + /* set preferred list of TLS 1.3 cipher suites for proxy */ + return Curl_setstropt(&data->set.str[STRING_SSL_CIPHER13_LIST_PROXY], + ptr); + else + return CURLE_NOT_BUILT_IN; break; #endif - case CURLOPT_KEYPASSWD: - /* - * String that holds the SSL or SSH private key password. - */ - result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD], - va_arg(param, char *)); + case CURLOPT_RANDOM_FILE: + break; + case CURLOPT_EGDSOCKET: break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_KEYPASSWD: + case CURLOPT_REQUEST_TARGET: + return Curl_setstropt(&data->set.str[STRING_TARGET], ptr); +#ifndef CURL_DISABLE_NETRC + case CURLOPT_NETRC_FILE: /* - * String that holds the SSL private key password for proxy. + * Use this file instead of the $HOME/.netrc file */ - result = Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_NETRC_FILE], ptr); #endif - case CURLOPT_SSLENGINE: + +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_MQTT) + case CURLOPT_COPYPOSTFIELDS: /* - * String that holds the SSL crypto engine. + * A string with POST data. Makes curl HTTP POST. Even if it is NULL. + * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to + * CURLOPT_COPYPOSTFIELDS and not altered later. */ - argptr = va_arg(param, char *); - if(argptr && argptr[0]) { - result = Curl_setstropt(&data->set.str[STRING_SSL_ENGINE], argptr); - if(!result) { - result = Curl_ssl_set_engine(data, argptr); + if(!ptr || data->set.postfieldsize == -1) + result = Curl_setstropt(&data->set.str[STRING_COPYPOSTFIELDS], ptr); + else { + if(data->set.postfieldsize < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; +#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T + /* + * Check that requested length does not overflow the size_t type. + */ + else if(data->set.postfieldsize > SIZE_T_MAX) + return CURLE_OUT_OF_MEMORY; +#endif + else { + /* Allocate even when size == 0. This satisfies the need of possible + later address compare to detect the COPYPOSTFIELDS mode, and to + mark that postfields is used rather than read function or form + data. + */ + char *p = Curl_memdup0(ptr, (size_t)data->set.postfieldsize); + if(!p) + return CURLE_OUT_OF_MEMORY; + else { + free(data->set.str[STRING_COPYPOSTFIELDS]); + data->set.str[STRING_COPYPOSTFIELDS] = p; + } } } + + data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; + data->set.method = HTTPREQ_POST; break; - case CURLOPT_SSLENGINE_DEFAULT: + case CURLOPT_POSTFIELDS: /* - * flag to set engine as default. + * Like above, but use static data instead of copying it. */ - Curl_safefree(data->set.str[STRING_SSL_ENGINE]); - result = Curl_ssl_set_engine_default(data); + data->set.postfields = ptr; + /* Release old copied data. */ + Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); + data->set.method = HTTPREQ_POST; break; - case CURLOPT_CRLF: + +#ifndef CURL_DISABLE_HTTP + case CURLOPT_ACCEPT_ENCODING: /* - * Kludgy option to enable CRLF conversions. Subject for removal. + * String to use at the value of Accept-Encoding header. + * + * If the encoding is set to "" we use an Accept-Encoding header that + * encompasses all the encodings we support. + * If the encoding is set to NULL we do not send an Accept-Encoding header + * and ignore an received Content-Encoding header. + * */ - data->set.crlf = (0 != va_arg(param, long)); - break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_HAPROXYPROTOCOL: + if(ptr && !*ptr) { + char all[256]; + Curl_all_content_encodings(all, sizeof(all)); + return Curl_setstropt(&data->set.str[STRING_ENCODING], all); + } + return Curl_setstropt(&data->set.str[STRING_ENCODING], ptr); + +#if !defined(CURL_DISABLE_AWS) + case CURLOPT_AWS_SIGV4: /* - * Set to send the HAProxy Proxy Protocol header + * String that is merged to some authentication + * parameters are used by the algorithm. */ - data->set.haproxyprotocol = (0 != va_arg(param, long)); - break; - case CURLOPT_HAPROXY_CLIENT_IP: + result = Curl_setstropt(&data->set.str[STRING_AWS_SIGV4], ptr); /* - * Set the client IP to send through HAProxy PROXY protocol + * Basic been set by default it need to be unset here */ - result = Curl_setstropt(&data->set.str[STRING_HAPROXY_CLIENT_IP], - va_arg(param, char *)); - /* We enable implicitly the HAProxy protocol if we use this flag. */ - data->set.haproxyprotocol = TRUE; + if(data->set.str[STRING_AWS_SIGV4]) + data->set.httpauth = CURLAUTH_AWS_SIGV4; break; #endif - 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. - */ - result = setstropt_interface(va_arg(param, char *), - &data->set.str[STRING_DEVICE], - &data->set.str[STRING_INTERFACE], - &data->set.str[STRING_BINDHOST]); - break; -#ifndef CURL_DISABLE_BINDLOCAL - case CURLOPT_LOCALPORT: + case CURLOPT_REFERER: /* - * Set what local port to bind the socket to when performing an operation. + * String to set in the HTTP Referer: field. */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.localport = curlx_sltous(arg); + if(data->state.referer_alloc) { + Curl_safefree(data->state.referer); + data->state.referer_alloc = FALSE; + } + result = Curl_setstropt(&data->set.str[STRING_SET_REFERER], ptr); + data->state.referer = data->set.str[STRING_SET_REFERER]; break; - case CURLOPT_LOCALPORTRANGE: + + case CURLOPT_USERAGENT: /* - * Set number of local ports to try, starting with CURLOPT_LOCALPORT. + * String to use in the HTTP User-Agent field */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 65535)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.localportrange = curlx_sltous(arg); - break; -#endif - case CURLOPT_GSSAPI_DELEGATION: + return Curl_setstropt(&data->set.str[STRING_USERAGENT], ptr); + +#if !defined(CURL_DISABLE_COOKIES) + case CURLOPT_COOKIE: /* - * GSS-API credential delegation bitmask + * Cookie string to send to the remote server in the request. */ - uarg = va_arg(param, unsigned long); - data->set.gssapi_delegation = (unsigned char)uarg& - (CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG); - break; - case CURLOPT_SSL_VERIFYPEER: + return Curl_setstropt(&data->set.str[STRING_COOKIE], ptr); + + case CURLOPT_COOKIEFILE: /* - * Enable peer SSL verifying. + * Set cookie file to read and parse. Can be used multiple times. */ - data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)); + if(ptr) { + struct curl_slist *cl; + /* general protection against mistakes and abuse */ + if(strlen(ptr) > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + /* append the cookie filename to the list of filenames, and deal with + them later */ + cl = curl_slist_append(data->state.cookielist, ptr); + if(!cl) { + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; + return CURLE_OUT_OF_MEMORY; + } + data->state.cookielist = cl; /* store the list for later use */ + } + else { + /* clear the list of cookie files */ + curl_slist_free_all(data->state.cookielist); + data->state.cookielist = NULL; - /* Update the current connection ssl_config. */ - Curl_ssl_conn_config_update(data, FALSE); + if(!data->share || !data->share->cookies) { + /* throw away all existing cookies if this is not a shared cookie + container */ + Curl_cookie_clearall(data->cookies); + Curl_cookie_cleanup(data->cookies); + } + /* disable the cookie engine */ + data->cookies = NULL; + } break; -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_SSL_VERIFYPEER: + + case CURLOPT_COOKIEJAR: /* - * Enable peer SSL verifying for DoH. + * Set cookie filename to dump all cookies to when we are done. */ - data->set.doh_verifypeer = (0 != va_arg(param, long)); + result = Curl_setstropt(&data->set.str[STRING_COOKIEJAR], ptr); + if(!result) { + /* + * Activate the cookie parser. This may or may not already + * have been made. + */ + struct CookieInfo *newcookies = + Curl_cookie_init(data, NULL, data->cookies, data->set.cookiesession); + if(!newcookies) + result = CURLE_OUT_OF_MEMORY; + data->cookies = newcookies; + } break; -#endif -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_VERIFYPEER: - /* - * Enable peer SSL verifying for proxy. - */ - data->set.proxy_ssl.primary.verifypeer = - (0 != va_arg(param, long)); - /* Update the current connection proxy_ssl_config. */ - Curl_ssl_conn_config_update(data, TRUE); - break; -#endif - case CURLOPT_SSL_VERIFYHOST: - /* - * Enable verification of the hostname in the peer certificate - */ - arg = va_arg(param, long); + case CURLOPT_COOKIELIST: + if(!ptr) + break; - /* Obviously people are not reading documentation and too many thought - this argument took a boolean when it was not and misused it. - Treat 1 and 2 the same */ - data->set.ssl.primary.verifyhost = !!(arg & 3); + if(strcasecompare(ptr, "ALL")) { + /* clear all cookies */ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_clearall(data->cookies); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + else if(strcasecompare(ptr, "SESS")) { + /* clear session cookies */ + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_clearsess(data->cookies); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } + else if(strcasecompare(ptr, "FLUSH")) { + /* flush cookies to file, takes care of the locking */ + Curl_flush_cookies(data, FALSE); + } + else if(strcasecompare(ptr, "RELOAD")) { + /* reload cookies from file */ + Curl_cookie_loadfiles(data); + break; + } + else { + if(!data->cookies) { + /* if cookie engine was not running, activate it */ + data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); + if(!data->cookies) + return CURLE_OUT_OF_MEMORY; + } - /* Update the current connection ssl_config. */ - Curl_ssl_conn_config_update(data, FALSE); + /* general protection against mistakes and abuse */ + if(strlen(ptr) > CURL_MAX_INPUT_LENGTH) + return CURLE_BAD_FUNCTION_ARGUMENT; + + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); + if(checkprefix("Set-Cookie:", ptr)) + /* HTTP Header format line */ + Curl_cookie_add(data, data->cookies, TRUE, FALSE, ptr + 11, NULL, + NULL, TRUE); + else + /* Netscape format line */ + Curl_cookie_add(data, data->cookies, FALSE, FALSE, ptr, NULL, + NULL, TRUE); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + } break; -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_SSL_VERIFYHOST: +#endif /* !CURL_DISABLE_COOKIES */ + +#endif /* ! CURL_DISABLE_HTTP */ + + case CURLOPT_CUSTOMREQUEST: /* - * Enable verification of the hostname in the peer certificate for DoH + * Set a custom string to use as request */ - arg = va_arg(param, long); + return Curl_setstropt(&data->set.str[STRING_CUSTOMREQUEST], ptr); + + /* we do not set + data->set.method = HTTPREQ_CUSTOM; + here, we continue as if we were using the already set type + and this just changes the actual request keyword */ - /* Treat both 1 and 2 as TRUE */ - data->set.doh_verifyhost = !!(arg & 3); - break; -#endif #ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_VERIFYHOST: + case CURLOPT_PROXY: /* - * Enable verification of the hostname in the peer certificate for proxy + * Set proxy server:port to use as proxy. + * + * If the proxy is set to "" (and CURLOPT_SOCKS_PROXY is set to "" or NULL) + * we explicitly say that we do not want to use a proxy + * (even though there might be environment variables saying so). + * + * Setting it to NULL, means no proxy but allows the environment variables + * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). */ - arg = va_arg(param, long); - - /* Treat both 1 and 2 as TRUE */ - data->set.proxy_ssl.primary.verifyhost = !!(arg & 3); - /* Update the current connection proxy_ssl_config. */ - Curl_ssl_conn_config_update(data, TRUE); + return Curl_setstropt(&data->set.str[STRING_PROXY], ptr); break; -#endif - case CURLOPT_SSL_VERIFYSTATUS: + + case CURLOPT_PRE_PROXY: /* - * Enable certificate status verifying. + * Set proxy server:port to use as SOCKS proxy. + * + * If the proxy is set to "" or NULL we explicitly say that we do not want + * to use the socks proxy. */ - if(!Curl_ssl_cert_status_request()) { - result = CURLE_NOT_BUILT_IN; - break; - } - - data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)); + return Curl_setstropt(&data->set.str[STRING_PRE_PROXY], ptr); +#endif /* CURL_DISABLE_PROXY */ - /* Update the current connection ssl_config. */ - Curl_ssl_conn_config_update(data, FALSE); - break; -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_SSL_VERIFYSTATUS: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_SOCKS5_GSSAPI_SERVICE: + case CURLOPT_PROXY_SERVICE_NAME: /* - * Enable certificate status verifying for DoH. + * Set proxy authentication service name for Kerberos 5 and SPNEGO */ - if(!Curl_ssl_cert_status_request()) { - result = CURLE_NOT_BUILT_IN; - break; - } - - data->set.doh_verifystatus = (0 != va_arg(param, long)); - break; + return Curl_setstropt(&data->set.str[STRING_PROXY_SERVICE_NAME], ptr); #endif - case CURLOPT_SSL_CTX_FUNCTION: + case CURLOPT_SERVICE_NAME: /* - * Set a SSL_CTX callback + * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) - data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); - else -#endif - result = CURLE_NOT_BUILT_IN; + return Curl_setstropt(&data->set.str[STRING_SERVICE_NAME], ptr); break; - case CURLOPT_SSL_CTX_DATA: + + case CURLOPT_HEADERDATA: /* - * Set a SSL_CTX callback parameter pointer + * Custom pointer to pass the header write callback function */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) - data->set.ssl.fsslctxp = va_arg(param, void *); - else -#endif - result = CURLE_NOT_BUILT_IN; + data->set.writeheader = (void *)ptr; break; - case CURLOPT_SSL_FALSESTART: + case CURLOPT_READDATA: /* - * Enable TLS false start. + * FILE pointer to read the file to be uploaded from. Or possibly used as + * argument to the read callback. */ - if(!Curl_ssl_false_start(data)) { - result = CURLE_NOT_BUILT_IN; - break; - } - - data->set.ssl.falsestart = (0 != va_arg(param, long)); - break; - case CURLOPT_CERTINFO: -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CERTINFO)) - data->set.ssl.certinfo = (0 != va_arg(param, long)); - else -#endif - result = CURLE_NOT_BUILT_IN; + data->set.in_set = (void *)ptr; break; - case CURLOPT_PINNEDPUBLICKEY: + case CURLOPT_WRITEDATA: /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. + * FILE pointer to write to. Or possibly used as argument to the write + * callback. */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; + data->set.out = (void *)ptr; break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_PINNEDPUBLICKEY: + case CURLOPT_DEBUGDATA: /* - * Set pinned public key for SSL connection. - * Specify filename of the public key in DER format. + * Set to a void * that should receive all error writes. This + * defaults to CURLOPT_STDERR for normal operations. */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) - result = Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; + data->set.debugdata = (void *)ptr; break; -#endif - case CURLOPT_CAINFO: + case CURLOPT_PROGRESSDATA: /* - * Set CA info for SSL connection. Specify filename of the CA certificate + * Custom client data to pass to the progress callback */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE], - va_arg(param, char *)); + data->set.progress_client = (void *)ptr; break; - case CURLOPT_CAINFO_BLOB: + case CURLOPT_SEEKDATA: /* - * Blob that holds CA info for SSL connection. - * Specify entire PEM of the CA certificate + * Seek control callback. Might be NULL. */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { - result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], - va_arg(param, struct curl_blob *)); - break; - } - else -#endif - return CURLE_NOT_BUILT_IN; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CAINFO: + data->set.seek_client = (void *)ptr; + break; + case CURLOPT_IOCTLDATA: /* - * Set CA info SSL connection for proxy. Specify filename of the - * CA certificate + * I/O control data pointer. Might be NULL. */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], - va_arg(param, char *)); + data->set.ioctl_client = (void *)ptr; break; - case CURLOPT_PROXY_CAINFO_BLOB: + case CURLOPT_SSL_CTX_DATA: /* - * Blob that holds CA info for SSL connection proxy. - * Specify entire PEM of the CA certificate + * Set a SSL_CTX callback parameter pointer */ #ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) { - result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], - va_arg(param, struct curl_blob *)); - break; - } + if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) + data->set.ssl.fsslctxp = (void *)ptr; else #endif return CURLE_NOT_BUILT_IN; -#endif - case CURLOPT_CAPATH: + break; + case CURLOPT_SOCKOPTDATA: /* - * Set CA path info for SSL connection. Specify directory name of the CA - * certificates which have been prepared using openssl c_rehash utility. + * socket callback data pointer. Might be NULL. */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) - /* This does not work on Windows. */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; + data->set.sockopt_client = (void *)ptr; break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CAPATH: + case CURLOPT_OPENSOCKETDATA: /* - * Set CA path info for SSL connection proxy. Specify directory name of the - * CA certificates which have been prepared using openssl c_rehash utility. + * socket callback data pointer. Might be NULL. */ -#ifdef USE_SSL - if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) - /* This does not work on Windows. */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], - va_arg(param, char *)); - else -#endif - result = CURLE_NOT_BUILT_IN; + data->set.opensocket_client = (void *)ptr; break; -#endif - case CURLOPT_CRLFILE: + case CURLOPT_RESOLVER_START_DATA: /* - * Set CRL file info for SSL connection. Specify filename of the CRL - * to check certificates revocation + * resolver start callback data pointer. Might be NULL. */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE], - va_arg(param, char *)); + data->set.resolver_start_client = (void *)ptr; break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_CRLFILE: + case CURLOPT_CLOSESOCKETDATA: /* - * Set CRL file info for SSL connection for proxy. Specify filename of the - * CRL to check certificates revocation + * socket callback data pointer. Might be NULL. */ - result = Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], - va_arg(param, char *)); + data->set.closesocket_client = (void *)ptr; break; + case CURLOPT_TRAILERDATA: +#ifndef CURL_DISABLE_HTTP + data->set.trailer_data = (void *)ptr; #endif - case CURLOPT_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT], - va_arg(param, char *)); break; - case CURLOPT_ISSUERCERT_BLOB: - /* - * Blob that holds Issuer certificate to check certificates issuer - */ - result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT], - va_arg(param, struct curl_blob *)); + case CURLOPT_PREREQDATA: + data->set.prereq_userp = (void *)ptr; break; -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_ISSUERCERT: + + case CURLOPT_ERRORBUFFER: /* - * Set Issuer certificate file - * to check certificates issuer + * Error buffer provided by the caller to get the human readable error + * string in. */ - result = Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_PROXY], - va_arg(param, char *)); + data->set.errorbuffer = ptr; break; - case CURLOPT_PROXY_ISSUERCERT_BLOB: + +#ifndef CURL_DISABLE_FTP + case CURLOPT_FTPPORT: /* - * Blob that holds Issuer certificate to check certificates issuer + * Use FTP PORT, this also specifies which IP address to use */ - result = Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY], - va_arg(param, struct curl_blob *)); + result = Curl_setstropt(&data->set.str[STRING_FTPPORT], ptr); + data->set.ftp_use_port = !!(data->set.str[STRING_FTPPORT]); break; -#endif -#ifndef CURL_DISABLE_TELNET - case CURLOPT_TELNETOPTIONS: + + case CURLOPT_FTP_ACCOUNT: + return Curl_setstropt(&data->set.str[STRING_FTP_ACCOUNT], ptr); + + case CURLOPT_FTP_ALTERNATIVE_TO_USER: + return Curl_setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER], ptr); + +#ifdef HAVE_GSSAPI + case CURLOPT_KRBLEVEL: /* - * Set a linked list of telnet options + * A string that defines the kerberos security level. */ - data->set.telnet_options = va_arg(param, struct curl_slist *); + result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL], ptr); + data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]); break; #endif - case CURLOPT_BUFFERSIZE: +#endif + case CURLOPT_URL: /* - * The application kindly asks for a differently sized receive buffer. - * If it seems reasonable, we will use it. + * The URL to fetch. */ - arg = va_arg(param, long); - - if(arg > READBUFFER_MAX) - arg = READBUFFER_MAX; - else if(arg < 1) - arg = READBUFFER_SIZE; - else if(arg < READBUFFER_MIN) - arg = READBUFFER_MIN; - - data->set.buffer_size = (unsigned int)arg; + if(data->state.url_alloc) { + /* the already set URL is allocated, free it first! */ + Curl_safefree(data->state.url); + data->state.url_alloc = FALSE; + } + result = Curl_setstropt(&data->set.str[STRING_SET_URL], ptr); + data->state.url = data->set.str[STRING_SET_URL]; break; - case CURLOPT_UPLOAD_BUFFERSIZE: + case CURLOPT_USERPWD: /* - * The application kindly asks for a differently sized upload buffer. - * Cap it to sensible. + * user:password to use in the operation */ - arg = va_arg(param, long); - - if(arg > UPLOADBUFFER_MAX) - arg = UPLOADBUFFER_MAX; - else if(arg < UPLOADBUFFER_MIN) - arg = UPLOADBUFFER_MIN; - - data->set.upload_buffer_size = (unsigned int)arg; - break; + return setstropt_userpwd(ptr, &data->set.str[STRING_USERNAME], + &data->set.str[STRING_PASSWORD]); - case CURLOPT_NOSIGNAL: + case CURLOPT_USERNAME: /* - * The application asks not to set any signal() or alarm() handlers, - * even when using a timeout. + * authentication username to use in the operation */ - data->set.no_signal = (0 != va_arg(param, long)); - break; - - case CURLOPT_SHARE: - { - struct Curl_share *set; - set = va_arg(param, struct Curl_share *); - - /* disconnect from old share, if any */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - if(data->dns.hostcachetype == HCACHE_SHARED) { - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies == data->cookies) - data->cookies = NULL; -#endif + return Curl_setstropt(&data->set.str[STRING_USERNAME], ptr); -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts == data->hsts) - data->hsts = NULL; -#endif -#ifdef USE_SSL - if(data->share->sslsession == data->state.session) - data->state.session = NULL; -#endif -#ifdef USE_LIBPSL - if(data->psl == &data->share->psl) - data->psl = data->multi ? &data->multi->psl : NULL; -#endif + case CURLOPT_PASSWORD: + /* + * authentication password to use in the operation + */ + return Curl_setstropt(&data->set.str[STRING_PASSWORD], ptr); - data->share->dirty--; + case CURLOPT_LOGIN_OPTIONS: + /* + * authentication options to use in the operation + */ + return Curl_setstropt(&data->set.str[STRING_OPTIONS], ptr); - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - data->share = NULL; - } + case CURLOPT_XOAUTH2_BEARER: + /* + * OAuth 2.0 bearer token to use in the operation + */ + return Curl_setstropt(&data->set.str[STRING_BEARER], ptr); - if(GOOD_SHARE_HANDLE(set)) - /* use new share if it set */ - data->share = set; - if(data->share) { +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXYUSERPWD: { + /* + * user:password needed to use the proxy + */ + char *u = NULL; + char *p = NULL; + result = setstropt_userpwd(ptr, &u, &p); - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + /* URL decode the components */ + if(!result && u) + result = Curl_urldecode(u, 0, &data->set.str[STRING_PROXYUSERNAME], NULL, + REJECT_ZERO); + if(!result && p) + result = Curl_urldecode(p, 0, &data->set.str[STRING_PROXYPASSWORD], NULL, + REJECT_ZERO); + free(u); + free(p); + } + break; + case CURLOPT_PROXYUSERNAME: + /* + * authentication username to use in the operation + */ + return Curl_setstropt(&data->set.str[STRING_PROXYUSERNAME], ptr); - data->share->dirty++; + case CURLOPT_PROXYPASSWORD: + /* + * authentication password to use in the operation + */ + return Curl_setstropt(&data->set.str[STRING_PROXYPASSWORD], ptr); - if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) { - /* use shared host cache */ - data->dns.hostcache = &data->share->hostcache; - data->dns.hostcachetype = HCACHE_SHARED; - } -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies) { - /* use shared cookie list, first free own one if any */ - Curl_cookie_cleanup(data->cookies); - /* enable cookies since we now use a share that uses cookies! */ - data->cookies = data->share->cookies; - } -#endif /* CURL_DISABLE_HTTP */ -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts) { - /* first free the private one if any */ - Curl_hsts_cleanup(&data->hsts); - data->hsts = data->share->hsts; - } -#endif -#ifdef USE_SSL - if(data->share->sslsession) { - data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions; - data->state.session = data->share->sslsession; - } -#endif -#ifdef USE_LIBPSL - if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) - data->psl = &data->share->psl; + case CURLOPT_NOPROXY: + /* + * proxy exception list + */ + return Curl_setstropt(&data->set.str[STRING_NOPROXY], ptr); #endif - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } - /* check for host cache not needed, - * it will be done by curl_easy_perform */ - } - break; - - case CURLOPT_PRIVATE: + case CURLOPT_RANGE: /* - * Set private data pointer. + * What range of the file you want to transfer */ - data->set.private_data = va_arg(param, void *); - break; + return Curl_setstropt(&data->set.str[STRING_SET_RANGE], ptr); - case CURLOPT_MAXFILESIZE: +#endif /* ! CURL_DISABLE_PROXY */ + case CURLOPT_CURLU: /* - * Set the maximum size of a file to download. + * pass CURLU to set URL */ - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.max_filesize = arg; + data->set.uh = (CURLU *)ptr; break; + case CURLOPT_SSLCERT: + /* + * String that holds filename of the SSL certificate to use + */ + return Curl_setstropt(&data->set.str[STRING_CERT], ptr); -#ifdef USE_SSL - case CURLOPT_USE_SSL: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLCERT: /* - * Make transfers attempt to use SSL/TLS. + * String that holds filename of the SSL certificate to use for proxy */ - arg = va_arg(param, long); - if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.use_ssl = (unsigned char)arg; - break; + return Curl_setstropt(&data->set.str[STRING_CERT_PROXY], ptr); - case CURLOPT_SSL_OPTIONS: - arg = va_arg(param, long); - data->set.ssl.primary.ssl_options = (unsigned char)(arg & 0xff); - data->set.ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); - data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); - data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); - data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); - data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); - /* If a setting is added here it should also be added in dohprobe() - which sets its own CURLOPT_SSL_OPTIONS based on these settings. */ - break; +#endif + case CURLOPT_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use + */ + return Curl_setstropt(&data->set.str[STRING_CERT_TYPE], ptr); #ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXY_SSL_OPTIONS: - arg = va_arg(param, long); - data->set.proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff); - data->set.proxy_ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST); - data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE); - data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN); - data->set.proxy_ssl.revoke_best_effort = - !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT); - data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA); - data->set.proxy_ssl.auto_client_cert = - !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT); - break; + case CURLOPT_PROXY_SSLCERTTYPE: + /* + * String that holds file type of the SSL certificate to use for proxy + */ + return Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY], ptr); #endif + case CURLOPT_SSLKEY: + /* + * String that holds filename of the SSL key to use + */ + return Curl_setstropt(&data->set.str[STRING_KEY], ptr); - case CURLOPT_SSL_EC_CURVES: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLKEY: /* - * Set accepted curves in SSL connection setup. - * Specify colon-delimited list of curve algorithm names. + * String that holds filename of the SSL key to use for proxy */ - result = Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_KEY_PROXY], ptr); + #endif - case CURLOPT_IPRESOLVE: - arg = va_arg(param, long); - if((arg < CURL_IPRESOLVE_WHATEVER) || (arg > CURL_IPRESOLVE_V6)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.ipver = (unsigned char) arg; + case CURLOPT_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use + */ + return Curl_setstropt(&data->set.str[STRING_KEY_TYPE], ptr); break; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLKEYTYPE: + /* + * String that holds file type of the SSL key to use for proxy + */ + return Curl_setstropt(&data->set.str[STRING_KEY_TYPE_PROXY], ptr); - case CURLOPT_MAXFILESIZE_LARGE: +#endif + case CURLOPT_KEYPASSWD: /* - * Set the maximum size of a file to download. + * String that holds the SSL or SSH private key password. */ - bigsize = va_arg(param, curl_off_t); - if(bigsize < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.max_filesize = bigsize; - break; + return Curl_setstropt(&data->set.str[STRING_KEY_PASSWD], ptr); - case CURLOPT_TCP_NODELAY: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_KEYPASSWD: /* - * Enable or disable TCP_NODELAY, which will disable/enable the Nagle - * algorithm + * String that holds the SSL private key password for proxy. + */ + return Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY], ptr); +#endif + case CURLOPT_SSLENGINE: + /* + * String that holds the SSL crypto engine. */ - data->set.tcp_nodelay = (0 != va_arg(param, long)); + if(ptr && ptr[0]) { + result = Curl_setstropt(&data->set.str[STRING_SSL_ENGINE], ptr); + if(!result) { + result = Curl_ssl_set_engine(data, ptr); + } + } break; - case CURLOPT_IGNORE_CONTENT_LENGTH: - data->set.ignorecl = (0 != va_arg(param, long)); +#ifndef CURL_DISABLE_PROXY + case CURLOPT_HAPROXY_CLIENT_IP: + /* + * Set the client IP to send through HAProxy PROXY protocol + */ + result = Curl_setstropt(&data->set.str[STRING_HAPROXY_CLIENT_IP], ptr); + /* enable the HAProxy protocol */ + data->set.haproxyprotocol = TRUE; break; +#endif + 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, + &data->set.str[STRING_DEVICE], + &data->set.str[STRING_INTERFACE], + &data->set.str[STRING_BINDHOST]); - case CURLOPT_CONNECT_ONLY: + case CURLOPT_PINNEDPUBLICKEY: /* - * No data transfer. - * (1) - only do connection - * (2) - do first get request but get no content + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. */ - arg = va_arg(param, long); - if(arg > 2) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.connect_only = (unsigned char)arg; - break; +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + return Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY], ptr); +#endif + return CURLE_NOT_BUILT_IN; - case CURLOPT_SOCKOPTFUNCTION: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_PINNEDPUBLICKEY: /* - * socket callback function: called after socket() but before connect() + * Set pinned public key for SSL connection. + * Specify filename of the public key in DER format. */ - data->set.fsockopt = va_arg(param, curl_sockopt_callback); - break; +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_PINNEDPUBKEY)) + return Curl_setstropt(&data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY], + ptr); +#endif + return CURLE_NOT_BUILT_IN; +#endif + case CURLOPT_CAINFO: + /* + * Set CA info for SSL connection. Specify filename of the CA certificate + */ + return Curl_setstropt(&data->set.str[STRING_SSL_CAFILE], ptr); - case CURLOPT_SOCKOPTDATA: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_CAINFO: /* - * socket callback data pointer. Might be NULL. + * Set CA info SSL connection for proxy. Specify filename of the + * CA certificate */ - data->set.sockopt_client = va_arg(param, void *); - break; + return Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], ptr); +#endif - case CURLOPT_OPENSOCKETFUNCTION: + case CURLOPT_CAPATH: /* - * open/create socket callback function: called instead of socket(), - * before connect() + * Set CA path info for SSL connection. Specify directory name of the CA + * certificates which have been prepared using openssl c_rehash utility. */ - data->set.fopensocket = va_arg(param, curl_opensocket_callback); - break; +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) + /* This does not work on Windows. */ + return Curl_setstropt(&data->set.str[STRING_SSL_CAPATH], ptr); +#endif + return CURLE_NOT_BUILT_IN; +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_CAPATH: + /* + * Set CA path info for SSL connection proxy. Specify directory name of the + * CA certificates which have been prepared using openssl c_rehash utility. + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CA_PATH)) + /* This does not work on Windows. */ + return Curl_setstropt(&data->set.str[STRING_SSL_CAPATH_PROXY], ptr); +#endif + return CURLE_NOT_BUILT_IN; +#endif + case CURLOPT_CRLFILE: + /* + * Set CRL file info for SSL connection. Specify filename of the CRL + * to check certificates revocation + */ + return Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE], ptr); - case CURLOPT_OPENSOCKETDATA: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_CRLFILE: /* - * socket callback data pointer. Might be NULL. + * Set CRL file info for SSL connection for proxy. Specify filename of the + * CRL to check certificates revocation */ - data->set.opensocket_client = va_arg(param, void *); - break; - - case CURLOPT_CLOSESOCKETFUNCTION: + return Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], ptr); +#endif + case CURLOPT_ISSUERCERT: /* - * close socket callback function: called instead of close() - * when shutting down a connection + * Set Issuer certificate file + * to check certificates issuer */ - data->set.fclosesocket = va_arg(param, curl_closesocket_callback); - break; + return Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT], ptr); - case CURLOPT_RESOLVER_START_FUNCTION: +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_ISSUERCERT: /* - * resolver start callback function: called before a new resolver request - * is started + * Set Issuer certificate file + * to check certificates issuer */ - data->set.resolver_start = va_arg(param, curl_resolver_start_callback); - break; + return Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_PROXY], ptr); - case CURLOPT_RESOLVER_START_DATA: +#endif + + case CURLOPT_PRIVATE: /* - * resolver start callback data pointer. Might be NULL. + * Set private data pointer. */ - data->set.resolver_start_client = va_arg(param, void *); + data->set.private_data = (void *)ptr; break; - case CURLOPT_CLOSESOCKETDATA: +#ifdef USE_SSL + case CURLOPT_SSL_EC_CURVES: /* - * socket callback data pointer. Might be NULL. + * Set accepted curves in SSL connection setup. + * Specify colon-delimited list of curve algorithm names. */ - data->set.closesocket_client = va_arg(param, void *); - break; - - case CURLOPT_SSL_SESSIONID_CACHE: - data->set.ssl.primary.cache_session = (0 != va_arg(param, long)); -#ifndef CURL_DISABLE_PROXY - data->set.proxy_ssl.primary.cache_session = - data->set.ssl.primary.cache_session; + return Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], ptr); #endif - break; - #ifdef USE_SSH - /* we only include SSH options if explicitly built to support SSH */ - case CURLOPT_SSH_AUTH_TYPES: - data->set.ssh_auth_types = (int)va_arg(param, long); - break; - case CURLOPT_SSH_PUBLIC_KEYFILE: /* * Use this file instead of the $HOME/.ssh/id_dsa.pub file */ - result = Curl_setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY], ptr); case CURLOPT_SSH_PRIVATE_KEYFILE: /* * Use this file instead of the $HOME/.ssh/id_dsa file */ - result = Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], ptr); + case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: /* * Option to allow for the MD5 of the host public key to be checked * for validation purposes. */ - result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], ptr); case CURLOPT_SSH_KNOWNHOSTS: /* * Store the filename to read known hosts from. */ - result = Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], - va_arg(param, char *)); + return Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], ptr); + + case CURLOPT_SSH_KEYDATA: + /* + * Custom client data to pass to the SSH keyfunc callback + */ + data->set.ssh_keyfunc_userp = (void *)ptr; break; #ifdef USE_LIBSSH2 case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256: @@ -2591,659 +2449,611 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * Option to allow for the SHA256 of the host public key to be checked * for validation purposes. */ - result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], - va_arg(param, char *)); - break; - - case CURLOPT_SSH_HOSTKEYFUNCTION: - /* the callback to check the hostkey without the knownhost file */ - data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback); - break; + return Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256], + ptr); case CURLOPT_SSH_HOSTKEYDATA: /* * Custom client data to pass to the SSH keyfunc callback */ - data->set.ssh_hostkeyfunc_userp = va_arg(param, void *); - break; -#endif - - case CURLOPT_SSH_KEYFUNCTION: - /* setting to NULL is fine since the ssh.c functions themselves will - then revert to use the internal default */ - data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback); - break; - - case CURLOPT_SSH_KEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - data->set.ssh_keyfunc_userp = va_arg(param, void *); - break; - - case CURLOPT_SSH_COMPRESSION: - data->set.ssh_compression = (0 != va_arg(param, long)); + data->set.ssh_hostkeyfunc_userp = (void *)ptr; break; +#endif /* USE_LIBSSH2 */ #endif /* USE_SSH */ - - case CURLOPT_HTTP_TRANSFER_DECODING: - /* - * disable libcurl transfer encoding is used - */ -#ifndef USE_HYPER - data->set.http_te_skip = (0 == va_arg(param, long)); - break; -#else - return CURLE_NOT_BUILT_IN; /* hyper does not support */ -#endif - - case CURLOPT_HTTP_CONTENT_DECODING: - /* - * raw data passed to the application when content encoding is used - */ - data->set.http_ce_skip = (0 == va_arg(param, long)); - break; - -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) - case CURLOPT_NEW_FILE_PERMS: - /* - * Uses these permissions instead of 0644 - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 0777)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.new_file_perms = (unsigned int)arg; - break; -#endif -#ifdef USE_SSH - case CURLOPT_NEW_DIRECTORY_PERMS: - /* - * Uses these permissions instead of 0755 - */ - arg = va_arg(param, long); - if((arg < 0) || (arg > 0777)) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.new_directory_perms = (unsigned int)arg; - break; -#endif - -#ifdef USE_IPV6 - case CURLOPT_ADDRESS_SCOPE: - /* - * Use this scope id when using IPv6 - * We always get longs when passed plain numericals so we should check - * that the value fits into an unsigned 32-bit integer. - */ - uarg = va_arg(param, unsigned long); -#if SIZEOF_LONG > 4 - if(uarg > UINT_MAX) - return CURLE_BAD_FUNCTION_ARGUMENT; -#endif - data->set.scope_id = (unsigned int)uarg; - break; -#endif - - case CURLOPT_PROTOCOLS: - /* set the bitmask for the protocols that are allowed to be used for the - transfer, which thus helps the app which takes URLs from users or other - external inputs and want to restrict what protocol(s) to deal - with. Defaults to CURLPROTO_ALL. */ - data->set.allowed_protocols = (curl_prot_t)va_arg(param, long); - break; - - case CURLOPT_REDIR_PROTOCOLS: - /* set the bitmask for the protocols that libcurl is allowed to follow to, - as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs - to be set in both bitmasks to be allowed to get redirected to. */ - data->set.redir_protocols = (curl_prot_t)va_arg(param, long); - break; - - case CURLOPT_PROTOCOLS_STR: { - argptr = va_arg(param, char *); - if(argptr) { - result = protocol2num(argptr, &data->set.allowed_protocols); - if(result) - return result; - } - else - /* make a NULL argument reset to default */ - data->set.allowed_protocols = (curl_prot_t) CURLPROTO_ALL; + case CURLOPT_PROTOCOLS_STR: + if(ptr) + return protocol2num(ptr, &data->set.allowed_protocols); + /* make a NULL argument reset to default */ + data->set.allowed_protocols = (curl_prot_t) CURLPROTO_ALL; break; - } - case CURLOPT_REDIR_PROTOCOLS_STR: { - argptr = va_arg(param, char *); - if(argptr) { - result = protocol2num(argptr, &data->set.redir_protocols); - if(result) - return result; - } - else - /* make a NULL argument reset to default */ - data->set.redir_protocols = (curl_prot_t) CURLPROTO_REDIR; + case CURLOPT_REDIR_PROTOCOLS_STR: + if(ptr) + return protocol2num(ptr, &data->set.redir_protocols); + /* make a NULL argument reset to default */ + data->set.redir_protocols = (curl_prot_t) CURLPROTO_REDIR; break; - } case CURLOPT_DEFAULT_PROTOCOL: /* Set the protocol to use when the URL does not include any protocol */ - result = Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_DEFAULT_PROTOCOL], ptr); + #ifndef CURL_DISABLE_SMTP case CURLOPT_MAIL_FROM: /* Set the SMTP mail originator */ - result = Curl_setstropt(&data->set.str[STRING_MAIL_FROM], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_MAIL_FROM], ptr); case CURLOPT_MAIL_AUTH: /* Set the SMTP auth originator */ - result = Curl_setstropt(&data->set.str[STRING_MAIL_AUTH], - va_arg(param, char *)); - break; - - case CURLOPT_MAIL_RCPT: - /* Set the list of mail recipients */ - data->set.mail_rcpt = va_arg(param, struct curl_slist *); - break; - case CURLOPT_MAIL_RCPT_ALLOWFAILS: - /* allow RCPT TO command to fail for some recipients */ - data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)); - break; + return Curl_setstropt(&data->set.str[STRING_MAIL_AUTH], ptr); #endif case CURLOPT_SASL_AUTHZID: /* Authorization identity (identity to act as) */ - result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], ptr); - case CURLOPT_SASL_IR: - /* Enable/disable SASL initial response */ - data->set.sasl_ir = (0 != va_arg(param, long)); - break; #ifndef CURL_DISABLE_RTSP - case CURLOPT_RTSP_REQUEST: - { - /* - * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) - * Would this be better if the RTSPREQ_* were just moved into here? - */ - long in_rtspreq = va_arg(param, long); - Curl_RtspReq rtspreq = RTSPREQ_NONE; - switch(in_rtspreq) { - case CURL_RTSPREQ_OPTIONS: - rtspreq = RTSPREQ_OPTIONS; - break; - - case CURL_RTSPREQ_DESCRIBE: - rtspreq = RTSPREQ_DESCRIBE; - break; - - case CURL_RTSPREQ_ANNOUNCE: - rtspreq = RTSPREQ_ANNOUNCE; - break; - - case CURL_RTSPREQ_SETUP: - rtspreq = RTSPREQ_SETUP; - break; - - case CURL_RTSPREQ_PLAY: - rtspreq = RTSPREQ_PLAY; - break; - - case CURL_RTSPREQ_PAUSE: - rtspreq = RTSPREQ_PAUSE; - break; - - case CURL_RTSPREQ_TEARDOWN: - rtspreq = RTSPREQ_TEARDOWN; - break; - - case CURL_RTSPREQ_GET_PARAMETER: - rtspreq = RTSPREQ_GET_PARAMETER; - break; - - case CURL_RTSPREQ_SET_PARAMETER: - rtspreq = RTSPREQ_SET_PARAMETER; - break; - - case CURL_RTSPREQ_RECORD: - rtspreq = RTSPREQ_RECORD; - break; - - case CURL_RTSPREQ_RECEIVE: - rtspreq = RTSPREQ_RECEIVE; - break; - default: - rtspreq = RTSPREQ_NONE; - } - - data->set.rtspreq = rtspreq; - break; - } - - case CURLOPT_RTSP_SESSION_ID: /* * Set the RTSP Session ID manually. Useful if the application is * resuming a previously established RTSP session */ - result = Curl_setstropt(&data->set.str[STRING_RTSP_SESSION_ID], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.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. */ - result = Curl_setstropt(&data->set.str[STRING_RTSP_STREAM_URI], - va_arg(param, char *)); + return Curl_setstropt(&data->set.str[STRING_RTSP_STREAM_URI], ptr); break; case CURLOPT_RTSP_TRANSPORT: /* * The content of the Transport: header for the RTSP request */ - result = Curl_setstropt(&data->set.str[STRING_RTSP_TRANSPORT], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_CLIENT_CSEQ: - /* - * Set the CSEQ number to issue for the next RTSP request. Useful if the - * application is resuming a previously broken connection. The CSEQ - * will increment from this new number henceforth. - */ - data->state.rtsp_next_client_CSeq = va_arg(param, long); - break; - - case CURLOPT_RTSP_SERVER_CSEQ: - /* Same as the above, but for server-initiated requests */ - data->state.rtsp_next_server_CSeq = va_arg(param, long); - break; + return Curl_setstropt(&data->set.str[STRING_RTSP_TRANSPORT], ptr); case CURLOPT_INTERLEAVEDATA: - data->set.rtp_out = va_arg(param, void *); - break; - case CURLOPT_INTERLEAVEFUNCTION: - /* Set the user defined RTP write function */ - data->set.fwrite_rtp = va_arg(param, curl_write_callback); + data->set.rtp_out = (void *)ptr; break; -#endif +#endif /* ! CURL_DISABLE_RTSP */ #ifndef CURL_DISABLE_FTP - case CURLOPT_WILDCARDMATCH: - data->set.wildcard_enabled = (0 != va_arg(param, long)); - break; - case CURLOPT_CHUNK_BGN_FUNCTION: - data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); - break; - case CURLOPT_CHUNK_END_FUNCTION: - data->set.chunk_end = va_arg(param, curl_chunk_end_callback); - break; - case CURLOPT_FNMATCH_FUNCTION: - data->set.fnmatch = va_arg(param, curl_fnmatch_callback); - break; case CURLOPT_CHUNK_DATA: - data->set.wildcardptr = va_arg(param, void *); + data->set.wildcardptr = (void *)ptr; break; case CURLOPT_FNMATCH_DATA: - data->set.fnmatch_data = va_arg(param, void *); + data->set.fnmatch_data = (void *)ptr; break; #endif #ifdef USE_TLS_SRP case CURLOPT_TLSAUTH_USERNAME: - result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME], ptr); + #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_USERNAME: - result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_TLSAUTH_USERNAME_PROXY], ptr); + #endif case CURLOPT_TLSAUTH_PASSWORD: - result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], ptr); + #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_PASSWORD: - result = Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], - va_arg(param, char *)); - break; + return Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], ptr); #endif case CURLOPT_TLSAUTH_TYPE: - argptr = va_arg(param, char *); - if(argptr && !strcasecompare(argptr, "SRP")) + if(ptr && !strcasecompare(ptr, "SRP")) return CURLE_BAD_FUNCTION_ARGUMENT; break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_TYPE: - argptr = va_arg(param, char *); - if(argptr && !strcasecompare(argptr, "SRP")) + if(ptr && !strcasecompare(ptr, "SRP")) return CURLE_BAD_FUNCTION_ARGUMENT; break; #endif #endif #ifdef USE_ARES case CURLOPT_DNS_SERVERS: - result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS], - va_arg(param, char *)); + result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS], ptr); if(result) return result; - result = Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]); - break; + return Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]); + case CURLOPT_DNS_INTERFACE: - result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE], - va_arg(param, char *)); + result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE], ptr); if(result) return result; - result = Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]); - break; + return Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]); + case CURLOPT_DNS_LOCAL_IP4: - result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4], - va_arg(param, char *)); + result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4], ptr); if(result) return result; - result = Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]); - break; + return Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]); + case CURLOPT_DNS_LOCAL_IP6: - result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6], - va_arg(param, char *)); + result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6], ptr); if(result) return result; - result = Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]); + return Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]); + +#endif +#ifdef USE_UNIX_SOCKETS + case CURLOPT_UNIX_SOCKET_PATH: + data->set.abstract_unix_socket = FALSE; + return Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], ptr); + + case CURLOPT_ABSTRACT_UNIX_SOCKET: + data->set.abstract_unix_socket = TRUE; + return Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], ptr); + +#endif + +#ifndef CURL_DISABLE_DOH + case CURLOPT_DOH_URL: + result = Curl_setstropt(&data->set.str[STRING_DOH], ptr); + data->set.doh = !!(data->set.str[STRING_DOH]); break; #endif - case CURLOPT_TCP_KEEPALIVE: - data->set.tcp_keepalive = (0 != va_arg(param, long)); +#ifndef CURL_DISABLE_HSTS + case CURLOPT_HSTSREADDATA: + data->set.hsts_read_userp = (void *)ptr; break; - case CURLOPT_TCP_KEEPIDLE: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; - data->set.tcp_keepidle = (int)arg; + case CURLOPT_HSTSWRITEDATA: + data->set.hsts_write_userp = (void *)ptr; break; - case CURLOPT_TCP_KEEPINTVL: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; - data->set.tcp_keepintvl = (int)arg; + case CURLOPT_HSTS: { + struct curl_slist *h; + if(!data->hsts) { + data->hsts = Curl_hsts_init(); + if(!data->hsts) + return CURLE_OUT_OF_MEMORY; + } + if(ptr) { + result = Curl_setstropt(&data->set.str[STRING_HSTS], ptr); + if(result) + return result; + /* this needs to build a list of filenames to read from, so that it can + read them later, as we might get a shared HSTS handle to load them + into */ + h = curl_slist_append(data->state.hstslist, ptr); + if(!h) { + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; + return CURLE_OUT_OF_MEMORY; + } + data->state.hstslist = h; /* store the list for later use */ + } + else { + /* clear the list of HSTS files */ + curl_slist_free_all(data->state.hstslist); + data->state.hstslist = NULL; + if(!data->share || !data->share->hsts) + /* throw away the HSTS cache unless shared */ + Curl_hsts_cleanup(&data->hsts); + } break; - case CURLOPT_TCP_KEEPCNT: - arg = va_arg(param, long); - if(arg < 0) + } +#endif /* ! CURL_DISABLE_HSTS */ +#ifndef CURL_DISABLE_ALTSVC + case CURLOPT_ALTSVC: + if(!data->asi) { + data->asi = Curl_altsvc_init(); + if(!data->asi) + return CURLE_OUT_OF_MEMORY; + } + result = Curl_setstropt(&data->set.str[STRING_ALTSVC], ptr); + if(result) + return result; + if(ptr) + (void)Curl_altsvc_load(data->asi, ptr); + break; +#endif /* ! CURL_DISABLE_ALTSVC */ +#ifdef USE_ECH + case CURLOPT_ECH: { + size_t plen = 0; + + if(!ptr) { + data->set.tls_ech = CURLECH_DISABLE; + return CURLE_OK; + } + plen = strlen(ptr); + if(plen > CURL_MAX_INPUT_LENGTH) { + data->set.tls_ech = CURLECH_DISABLE; return CURLE_BAD_FUNCTION_ARGUMENT; - else if(arg > INT_MAX) - arg = INT_MAX; - data->set.tcp_keepcnt = (int)arg; + } + /* set tls_ech flag value, preserving CLA_CFG bit */ + if(!strcmp(ptr, "false")) + data->set.tls_ech = CURLECH_DISABLE | + (data->set.tls_ech & CURLECH_CLA_CFG); + else if(!strcmp(ptr, "grease")) + data->set.tls_ech = CURLECH_GREASE | + (data->set.tls_ech & CURLECH_CLA_CFG); + else if(!strcmp(ptr, "true")) + data->set.tls_ech = CURLECH_ENABLE | + (data->set.tls_ech & CURLECH_CLA_CFG); + else if(!strcmp(ptr, "hard")) + data->set.tls_ech = CURLECH_HARD | + (data->set.tls_ech & CURLECH_CLA_CFG); + else if(plen > 5 && !strncmp(ptr, "ecl:", 4)) { + result = Curl_setstropt(&data->set.str[STRING_ECH_CONFIG], ptr + 4); + if(result) + return result; + data->set.tls_ech |= CURLECH_CLA_CFG; + } + else if(plen > 4 && !strncmp(ptr, "pn:", 3)) { + result = Curl_setstropt(&data->set.str[STRING_ECH_PUBLIC], ptr + 3); + if(result) + return result; + } break; - case CURLOPT_TCP_FASTOPEN: -#if defined(CONNECT_DATA_IDEMPOTENT) || defined(MSG_FASTOPEN) || \ - defined(TCP_FASTOPEN_CONNECT) - data->set.tcp_fastopen = (0 != va_arg(param, long)); -#else - result = CURLE_NOT_BUILT_IN; + } #endif + default: + return CURLE_UNKNOWN_OPTION; + } + return result; +} + +static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, + va_list param) +{ + switch(option) { + case CURLOPT_PROGRESSFUNCTION: + /* + * Progress callback function + */ + data->set.fprogress = va_arg(param, curl_progress_callback); + if(data->set.fprogress) + data->progress.callback = TRUE; /* no longer internal */ + else + data->progress.callback = FALSE; /* NULL enforces internal */ break; - case CURLOPT_SSL_ENABLE_NPN: + + case CURLOPT_XFERINFOFUNCTION: + /* + * Transfer info callback function + */ + data->set.fxferinfo = va_arg(param, curl_xferinfo_callback); + if(data->set.fxferinfo) + data->progress.callback = TRUE; /* no longer internal */ + else + data->progress.callback = FALSE; /* NULL enforces internal */ + break; - case CURLOPT_SSL_ENABLE_ALPN: - data->set.ssl_enable_alpn = (0 != va_arg(param, long)); + case CURLOPT_DEBUGFUNCTION: + /* + * stderr write callback. + */ + data->set.fdebug = va_arg(param, curl_debug_callback); + /* + * if the callback provided is NULL, it will use the default callback + */ break; -#ifdef USE_UNIX_SOCKETS - case CURLOPT_UNIX_SOCKET_PATH: - data->set.abstract_unix_socket = FALSE; - result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], - va_arg(param, char *)); + case CURLOPT_HEADERFUNCTION: + /* + * Set header write callback + */ + data->set.fwrite_header = va_arg(param, curl_write_callback); break; - case CURLOPT_ABSTRACT_UNIX_SOCKET: - data->set.abstract_unix_socket = TRUE; - result = Curl_setstropt(&data->set.str[STRING_UNIX_SOCKET_PATH], - va_arg(param, char *)); + case CURLOPT_WRITEFUNCTION: + /* + * Set data write callback + */ + data->set.fwrite_func = va_arg(param, curl_write_callback); + if(!data->set.fwrite_func) + /* When set to NULL, reset to our internal default function */ + data->set.fwrite_func = (curl_write_callback)fwrite; break; -#endif - - case CURLOPT_PATH_AS_IS: - data->set.path_as_is = (0 != va_arg(param, long)); + case CURLOPT_READFUNCTION: + /* + * Read data callback + */ + data->set.fread_func_set = va_arg(param, curl_read_callback); + if(!data->set.fread_func_set) { + data->set.is_fread_set = 0; + /* When set to NULL, reset to our internal default function */ + data->set.fread_func_set = (curl_read_callback)fread; + } + else + data->set.is_fread_set = 1; break; - case CURLOPT_PIPEWAIT: - data->set.pipewait = (0 != va_arg(param, long)); + case CURLOPT_SEEKFUNCTION: + /* + * Seek callback. Might be NULL. + */ + data->set.seek_func = va_arg(param, curl_seek_callback); break; - case CURLOPT_STREAM_WEIGHT: -#if defined(USE_HTTP2) || defined(USE_HTTP3) - arg = va_arg(param, long); - if((arg >= 1) && (arg <= 256)) - data->set.priority.weight = (int)arg; + case CURLOPT_IOCTLFUNCTION: + /* + * I/O control callback. Might be NULL. + */ + data->set.ioctl_func = va_arg(param, curl_ioctl_callback); break; -#else - return CURLE_NOT_BUILT_IN; + case CURLOPT_SSL_CTX_FUNCTION: + /* + * Set a SSL_CTX callback + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) + data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); + else #endif - 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); - } + return CURLE_NOT_BUILT_IN; break; - } - case CURLOPT_CONNECT_TO: - data->set.connect_to = va_arg(param, struct curl_slist *); + + case CURLOPT_SOCKOPTFUNCTION: + /* + * socket callback function: called after socket() but before connect() + */ + data->set.fsockopt = va_arg(param, curl_sockopt_callback); break; - case CURLOPT_SUPPRESS_CONNECT_HEADERS: - data->set.suppress_connect_headers = (0 != va_arg(param, long)); + + case CURLOPT_OPENSOCKETFUNCTION: + /* + * open/create socket callback function: called instead of socket(), + * before connect() + */ + data->set.fopensocket = va_arg(param, curl_opensocket_callback); break; - case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: - uarg = va_arg(param, unsigned long); - if(uarg > UINT_MAX) - uarg = UINT_MAX; - data->set.happy_eyeballs_timeout = (unsigned int)uarg; + + case CURLOPT_CLOSESOCKETFUNCTION: + /* + * close socket callback function: called instead of close() + * when shutting down a connection + */ + data->set.fclosesocket = va_arg(param, curl_closesocket_callback); break; -#ifndef CURL_DISABLE_SHUFFLE_DNS - case CURLOPT_DNS_SHUFFLE_ADDRESSES: - data->set.dns_shuffle_addresses = (0 != va_arg(param, long)); + + case CURLOPT_RESOLVER_START_FUNCTION: + /* + * resolver start callback function: called before a new resolver request + * is started + */ + data->set.resolver_start = va_arg(param, curl_resolver_start_callback); + break; + + +#ifdef USE_SSH +#ifdef USE_LIBSSH2 + case CURLOPT_SSH_HOSTKEYFUNCTION: + /* the callback to check the hostkey without the knownhost file */ + data->set.ssh_hostkeyfunc = va_arg(param, curl_sshhostkeycallback); break; #endif - case CURLOPT_DISALLOW_USERNAME_IN_URL: - data->set.disallow_username_in_url = (0 != va_arg(param, long)); + + case CURLOPT_SSH_KEYFUNCTION: + /* setting to NULL is fine since the ssh.c functions themselves will + then revert to use the internal default */ + data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback); break; -#ifndef CURL_DISABLE_DOH - case CURLOPT_DOH_URL: - result = Curl_setstropt(&data->set.str[STRING_DOH], - va_arg(param, char *)); - data->set.doh = !!(data->set.str[STRING_DOH]); + +#endif /* USE_SSH */ + +#ifndef CURL_DISABLE_RTSP + case CURLOPT_INTERLEAVEFUNCTION: + /* Set the user defined RTP write function */ + data->set.fwrite_rtp = va_arg(param, curl_write_callback); break; #endif - case CURLOPT_UPKEEP_INTERVAL_MS: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.upkeep_interval_ms = arg; +#ifndef CURL_DISABLE_FTP + case CURLOPT_CHUNK_BGN_FUNCTION: + data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); break; - case CURLOPT_MAXAGE_CONN: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxage_conn = arg; + case CURLOPT_CHUNK_END_FUNCTION: + data->set.chunk_end = va_arg(param, curl_chunk_end_callback); break; - case CURLOPT_MAXLIFETIME_CONN: - arg = va_arg(param, long); - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxlifetime_conn = arg; + case CURLOPT_FNMATCH_FUNCTION: + data->set.fnmatch = va_arg(param, curl_fnmatch_callback); break; - case CURLOPT_TRAILERFUNCTION: +#endif #ifndef CURL_DISABLE_HTTP + case CURLOPT_TRAILERFUNCTION: data->set.trailer_callback = va_arg(param, curl_trailer_callback); -#endif break; - case CURLOPT_TRAILERDATA: -#ifndef CURL_DISABLE_HTTP - data->set.trailer_data = va_arg(param, void *); #endif - break; #ifndef CURL_DISABLE_HSTS case CURLOPT_HSTSREADFUNCTION: data->set.hsts_read = va_arg(param, curl_hstsread_callback); break; - case CURLOPT_HSTSREADDATA: - data->set.hsts_read_userp = va_arg(param, void *); - break; case CURLOPT_HSTSWRITEFUNCTION: data->set.hsts_write = va_arg(param, curl_hstswrite_callback); break; - case CURLOPT_HSTSWRITEDATA: - data->set.hsts_write_userp = va_arg(param, void *); - break; - case CURLOPT_HSTS: { - struct curl_slist *h; - if(!data->hsts) { - data->hsts = Curl_hsts_init(); - if(!data->hsts) - return CURLE_OUT_OF_MEMORY; - } - argptr = va_arg(param, char *); - if(argptr) { - result = Curl_setstropt(&data->set.str[STRING_HSTS], argptr); - if(result) - return result; - /* this needs to build a list of filenames to read from, so that it can - read them later, as we might get a shared HSTS handle to load them - into */ - h = curl_slist_append(data->state.hstslist, argptr); - if(!h) { - curl_slist_free_all(data->state.hstslist); - data->state.hstslist = NULL; - return CURLE_OUT_OF_MEMORY; - } - data->state.hstslist = h; /* store the list for later use */ - } - else { - /* clear the list of HSTS files */ - curl_slist_free_all(data->state.hstslist); - data->state.hstslist = NULL; - if(!data->share || !data->share->hsts) - /* throw away the HSTS cache unless shared */ - Curl_hsts_cleanup(&data->hsts); - } +#endif + case CURLOPT_PREREQFUNCTION: + data->set.fprereq = va_arg(param, curl_prereq_callback); break; + default: + return CURLE_UNKNOWN_OPTION; } - case CURLOPT_HSTS_CTRL: - arg = va_arg(param, long); - if(arg & CURLHSTS_ENABLE) { - if(!data->hsts) { - data->hsts = Curl_hsts_init(); - if(!data->hsts) - return CURLE_OUT_OF_MEMORY; - } - } - else - Curl_hsts_cleanup(&data->hsts); + return CURLE_OK; +} + +static CURLcode setopt_offt(struct Curl_easy *data, CURLoption option, + curl_off_t offt) +{ + switch(option) { + case CURLOPT_TIMEVALUE_LARGE: + /* + * This is the value to compare with the remote document with the + * method set with CURLOPT_TIMECONDITION + */ + data->set.timevalue = (time_t)offt; break; -#endif -#ifndef CURL_DISABLE_ALTSVC - case CURLOPT_ALTSVC: - if(!data->asi) { - data->asi = Curl_altsvc_init(); - if(!data->asi) - return CURLE_OUT_OF_MEMORY; + + /* MQTT "borrows" some of the HTTP options */ + case CURLOPT_POSTFIELDSIZE_LARGE: + /* + * The size of the POSTFIELD data to prevent libcurl to do strlen() to + * figure it out. Enables binary posts. + */ + if(offt < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + + if(data->set.postfieldsize < offt && + data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { + /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ + Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]); + data->set.postfields = NULL; } - argptr = va_arg(param, char *); - result = Curl_setstropt(&data->set.str[STRING_ALTSVC], argptr); - if(result) - return result; - if(argptr) - (void)Curl_altsvc_load(data->asi, argptr); + data->set.postfieldsize = offt; break; - case CURLOPT_ALTSVC_CTRL: - if(!data->asi) { - data->asi = Curl_altsvc_init(); - if(!data->asi) - return CURLE_OUT_OF_MEMORY; - } - arg = va_arg(param, long); - if(!arg) { - DEBUGF(infof(data, "bad CURLOPT_ALTSVC_CTRL input")); + case CURLOPT_INFILESIZE_LARGE: + /* + * If known, this should inform curl about the file size of the + * to-be-uploaded file. + */ + if(offt < -1) return CURLE_BAD_FUNCTION_ARGUMENT; - } - result = Curl_altsvc_ctrl(data->asi, arg); - if(result) - return result; + data->set.filesize = offt; break; -#endif - case CURLOPT_PREREQFUNCTION: - data->set.fprereq = va_arg(param, curl_prereq_callback); + case CURLOPT_MAX_SEND_SPEED_LARGE: + /* + * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE + * bytes per second the transfer is throttled.. + */ + if(offt < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_send_speed = offt; break; - case CURLOPT_PREREQDATA: - data->set.prereq_userp = va_arg(param, void *); + case CURLOPT_MAX_RECV_SPEED_LARGE: + /* + * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per + * second the transfer is throttled.. + */ + if(offt < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.max_recv_speed = offt; break; -#ifndef CURL_DISABLE_WEBSOCKETS - case CURLOPT_WS_OPTIONS: { - bool raw; - arg = va_arg(param, long); - raw = (arg & CURLWS_RAW_MODE); - data->set.ws_raw_mode = raw; + case CURLOPT_RESUME_FROM_LARGE: + /* + * Resume transfer at the given file position + */ + if(offt < -1) + return CURLE_BAD_FUNCTION_ARGUMENT; + data->set.set_resume_from = offt; break; - } -#endif -#ifdef USE_ECH - case CURLOPT_ECH: { - size_t plen = 0; - - argptr = va_arg(param, char *); - if(!argptr) { - data->set.tls_ech = CURLECH_DISABLE; - return CURLE_OK; - } - plen = strlen(argptr); - if(plen > CURL_MAX_INPUT_LENGTH) { - data->set.tls_ech = CURLECH_DISABLE; + case CURLOPT_MAXFILESIZE_LARGE: + /* + * Set the maximum size of a file to download. + */ + if(offt < 0) return CURLE_BAD_FUNCTION_ARGUMENT; - } - /* set tls_ech flag value, preserving CLA_CFG bit */ - if(plen == 5 && !strcmp(argptr, "false")) - data->set.tls_ech = CURLECH_DISABLE | - (data->set.tls_ech & CURLECH_CLA_CFG); - else if(plen == 6 && !strcmp(argptr, "grease")) - data->set.tls_ech = CURLECH_GREASE | - (data->set.tls_ech & CURLECH_CLA_CFG); - else if(plen == 4 && !strcmp(argptr, "true")) - data->set.tls_ech = CURLECH_ENABLE | - (data->set.tls_ech & CURLECH_CLA_CFG); - else if(plen == 4 && !strcmp(argptr, "hard")) - data->set.tls_ech = CURLECH_HARD | - (data->set.tls_ech & CURLECH_CLA_CFG); - else if(plen > 5 && !strncmp(argptr, "ecl:", 4)) { - result = Curl_setstropt(&data->set.str[STRING_ECH_CONFIG], argptr + 4); - if(result) - return result; - data->set.tls_ech |= CURLECH_CLA_CFG; - } - else if(plen > 4 && !strncmp(argptr, "pn:", 3)) { - result = Curl_setstropt(&data->set.str[STRING_ECH_PUBLIC], argptr + 3); - if(result) - return result; - } + data->set.max_filesize = offt; break; + + default: + return CURLE_UNKNOWN_OPTION; } + return CURLE_OK; +} + +static CURLcode setopt_blob(struct Curl_easy *data, CURLoption option, + struct curl_blob *blob) +{ + switch(option) { + case CURLOPT_SSLCERT_BLOB: + /* + * Blob that holds file content of the SSL certificate to use + */ + return Curl_setblobopt(&data->set.blobs[BLOB_CERT], blob); +#ifndef CURL_DISABLE_PROXY + case CURLOPT_PROXY_SSLCERT_BLOB: + /* + * Blob that holds file content of the SSL certificate to use for proxy + */ + return Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY], blob); + case CURLOPT_PROXY_SSLKEY_BLOB: + /* + * Blob that holds file content of the SSL key to use for proxy + */ + return Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY], blob); + case CURLOPT_PROXY_CAINFO_BLOB: + /* + * Blob that holds CA info for SSL connection proxy. + * Specify entire PEM of the CA certificate + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + return Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY], blob); #endif - case CURLOPT_QUICK_EXIT: - data->set.quick_exit = (0 != va_arg(param, long)) ? 1L : 0L; - break; + return CURLE_NOT_BUILT_IN; + case CURLOPT_PROXY_ISSUERCERT_BLOB: + /* + * Blob that holds Issuer certificate to check certificates issuer + */ + return Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY], + blob); +#endif + case CURLOPT_SSLKEY_BLOB: + /* + * Blob that holds file content of the SSL key to use + */ + return Curl_setblobopt(&data->set.blobs[BLOB_KEY], blob); + case CURLOPT_CAINFO_BLOB: + /* + * Blob that holds CA info for SSL connection. + * Specify entire PEM of the CA certificate + */ +#ifdef USE_SSL + if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) + return Curl_setblobopt(&data->set.blobs[BLOB_CAINFO], blob); +#endif + return CURLE_NOT_BUILT_IN; + case CURLOPT_ISSUERCERT_BLOB: + /* + * Blob that holds Issuer certificate to check certificates issuer + */ + return Curl_setblobopt(&data->set.blobs[BLOB_SSL_ISSUERCERT], blob); + default: - /* unknown tag and its companion, just ignore: */ - result = CURLE_UNKNOWN_OPTION; - break; + return CURLE_UNKNOWN_OPTION; } + /* unreachable */ +} - return result; +/* + * Do not make Curl_vsetopt() static: it is called from + * packages/OS400/ccsidcurl.c. + */ +CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) +{ + if(option < CURLOPTTYPE_OBJECTPOINT) + return setopt_long(data, option, va_arg(param, long)); + else if(option < CURLOPTTYPE_FUNCTIONPOINT) { + /* unfortunately, different pointer types cannot be identified any other + way than being listed explicitly */ + switch(option) { + case CURLOPT_HTTPHEADER: + case CURLOPT_QUOTE: + case CURLOPT_POSTQUOTE: + case CURLOPT_TELNETOPTIONS: + case CURLOPT_PREQUOTE: + case CURLOPT_HTTP200ALIASES: + case CURLOPT_MAIL_RCPT: + case CURLOPT_RESOLVE: + case CURLOPT_PROXYHEADER: + case CURLOPT_CONNECT_TO: + return setopt_slist(data, option, va_arg(param, struct curl_slist *)); + case CURLOPT_HTTPPOST: /* curl_httppost * */ + case CURLOPT_MIMEPOST: /* curl_mime * */ + case CURLOPT_STDERR: /* FILE * */ + case CURLOPT_SHARE: /* CURLSH * */ + case CURLOPT_STREAM_DEPENDS: /* CURL * */ + case CURLOPT_STREAM_DEPENDS_E: /* CURL * */ + return setopt_pointers(data, option, param); + default: + break; + } + /* the char pointer options */ + return setopt_cptr(data, option, va_arg(param, char *)); + } + else if(option < CURLOPTTYPE_OFF_T) + return setopt_func(data, option, param); + else if(option < CURLOPTTYPE_BLOB) + return setopt_offt(data, option, va_arg(param, curl_off_t)); + return setopt_blob(data, option, va_arg(param, struct curl_blob *)); } /* @@ -3255,10 +3065,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) */ #undef curl_easy_setopt -CURLcode curl_easy_setopt(struct Curl_easy *data, CURLoption tag, ...) +CURLcode curl_easy_setopt(CURL *d, CURLoption tag, ...) { va_list arg; CURLcode result; + struct Curl_easy *data = d; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; diff --git a/lib/sha256.c b/lib/sha256.c index dedeae42e9076d..c5bb921bb3029c 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -34,9 +34,6 @@ #ifdef USE_WOLFSSL #include -#ifndef NO_SHA256 -#define USE_OPENSSL_SHA256 -#endif #endif #if defined(USE_OPENSSL) @@ -105,8 +102,9 @@ struct ossl_sha256_ctx { }; typedef struct ossl_sha256_ctx my_sha256_ctx; -static CURLcode my_sha256_init(my_sha256_ctx *ctx) +static CURLcode my_sha256_init(void *in) { + my_sha256_ctx *ctx = (my_sha256_ctx *)in; ctx->openssl_ctx = EVP_MD_CTX_create(); if(!ctx->openssl_ctx) return CURLE_OUT_OF_MEMORY; @@ -118,15 +116,17 @@ static CURLcode my_sha256_init(my_sha256_ctx *ctx) return CURLE_OK; } -static void my_sha256_update(my_sha256_ctx *ctx, +static void my_sha256_update(void *in, const unsigned char *data, unsigned int length) { + my_sha256_ctx *ctx = (my_sha256_ctx *)in; EVP_DigestUpdate(ctx->openssl_ctx, data, length); } -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +static void my_sha256_final(unsigned char *digest, void *in) { + my_sha256_ctx *ctx = (my_sha256_ctx *)in; EVP_DigestFinal_ex(ctx->openssl_ctx, digest, NULL); EVP_MD_CTX_destroy(ctx->openssl_ctx); } @@ -135,20 +135,20 @@ static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) typedef struct sha256_ctx my_sha256_ctx; -static CURLcode my_sha256_init(my_sha256_ctx *ctx) +static CURLcode my_sha256_init(void *ctx) { sha256_init(ctx); return CURLE_OK; } -static void my_sha256_update(my_sha256_ctx *ctx, +static void my_sha256_update(void *ctx, const unsigned char *data, unsigned int length) { sha256_update(ctx, length, data); } -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +static void my_sha256_final(unsigned char *digest, void *ctx) { sha256_digest(ctx, SHA256_DIGEST_SIZE, digest); } @@ -157,7 +157,7 @@ static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) typedef mbedtls_sha256_context my_sha256_ctx; -static CURLcode my_sha256_init(my_sha256_ctx *ctx) +static CURLcode my_sha256_init(void *ctx) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_sha256_starts(ctx, 0); @@ -167,7 +167,7 @@ static CURLcode my_sha256_init(my_sha256_ctx *ctx) return CURLE_OK; } -static void my_sha256_update(my_sha256_ctx *ctx, +static void my_sha256_update(void *ctx, const unsigned char *data, unsigned int length) { @@ -178,7 +178,7 @@ static void my_sha256_update(my_sha256_ctx *ctx, #endif } -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +static void my_sha256_final(unsigned char *digest, void *ctx) { #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS) (void) mbedtls_sha256_finish(ctx, digest); @@ -190,20 +190,20 @@ static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) #elif defined(AN_APPLE_OS) typedef CC_SHA256_CTX my_sha256_ctx; -static CURLcode my_sha256_init(my_sha256_ctx *ctx) +static CURLcode my_sha256_init(void *ctx) { (void) CC_SHA256_Init(ctx); return CURLE_OK; } -static void my_sha256_update(my_sha256_ctx *ctx, +static void my_sha256_update(void *ctx, const unsigned char *data, unsigned int length) { (void) CC_SHA256_Update(ctx, data, length); } -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +static void my_sha256_final(unsigned char *digest, void *ctx) { (void) CC_SHA256_Final(digest, ctx); } @@ -220,8 +220,9 @@ typedef struct sha256_ctx my_sha256_ctx; #define CALG_SHA_256 0x0000800c #endif -static CURLcode my_sha256_init(my_sha256_ctx *ctx) +static CURLcode my_sha256_init(void *in) { + my_sha256_ctx *ctx = (my_sha256_ctx *)in; if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) return CURLE_OUT_OF_MEMORY; @@ -235,15 +236,17 @@ static CURLcode my_sha256_init(my_sha256_ctx *ctx) return CURLE_OK; } -static void my_sha256_update(my_sha256_ctx *ctx, +static void my_sha256_update(void *in, const unsigned char *data, unsigned int length) { + my_sha256_ctx *ctx = (my_sha256_ctx *)in; CryptHashData(ctx->hHash, (unsigned char *) data, length, 0); } -static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx) +static void my_sha256_final(unsigned char *digest, void *in) { + my_sha256_ctx *ctx = (my_sha256_ctx *)in; unsigned long length = 0; CryptGetHashParam(ctx->hHash, HP_HASHVAL, NULL, &length, 0); @@ -388,8 +391,9 @@ static int sha256_compress(struct sha256_state *md, } /* Initialize the hash state */ -static CURLcode my_sha256_init(struct sha256_state *md) +static CURLcode my_sha256_init(void *in) { + struct sha256_state *md = (struct sha256_state *)in; md->curlen = 0; md->length = 0; md->state[0] = 0x6A09E667UL; @@ -409,21 +413,21 @@ static CURLcode my_sha256_init(struct sha256_state *md) @param md The hash state @param in The data to hash @param inlen The length of the data (octets) - @return 0 if successful */ -static int my_sha256_update(struct sha256_state *md, - const unsigned char *in, - unsigned long inlen) +static void my_sha256_update(void *ctx, + const unsigned char *in, + unsigned int len) { + unsigned long inlen = len; unsigned long n; - + struct sha256_state *md = (struct sha256_state *)ctx; #define CURL_SHA256_BLOCK_SIZE 64 if(md->curlen > sizeof(md->buf)) - return -1; + return; while(inlen > 0) { if(md->curlen == 0 && inlen >= CURL_SHA256_BLOCK_SIZE) { if(sha256_compress(md, (unsigned char *)in) < 0) - return -1; + return; md->length += CURL_SHA256_BLOCK_SIZE * 8; in += CURL_SHA256_BLOCK_SIZE; inlen -= CURL_SHA256_BLOCK_SIZE; @@ -436,14 +440,12 @@ static int my_sha256_update(struct sha256_state *md, inlen -= n; if(md->curlen == CURL_SHA256_BLOCK_SIZE) { if(sha256_compress(md, md->buf) < 0) - return -1; + return; md->length += 8 * CURL_SHA256_BLOCK_SIZE; md->curlen = 0; } } } - - return 0; } /* @@ -452,13 +454,13 @@ static int my_sha256_update(struct sha256_state *md, @param out [out] The destination of the hash (32 bytes) @return 0 if successful */ -static int my_sha256_final(unsigned char *out, - struct sha256_state *md) +static void my_sha256_final(unsigned char *out, void *ctx) { + struct sha256_state *md = ctx; int i; if(md->curlen >= sizeof(md->buf)) - return -1; + return; /* Increase the length of the message */ md->length += md->curlen * 8; @@ -490,8 +492,6 @@ static int my_sha256_final(unsigned char *out, /* Copy output */ for(i = 0; i < 8; i++) WPA_PUT_BE32(out + (4 * i), md->state[i]); - - return 0; } #endif /* CRYPTO LIBS */ @@ -510,7 +510,7 @@ static int my_sha256_final(unsigned char *out, * Returns CURLE_OK on success. */ CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, - const size_t length) + const size_t length) { CURLcode result; my_sha256_ctx ctx; @@ -524,21 +524,13 @@ CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, } -const struct HMAC_params Curl_HMAC_SHA256[] = { - { - /* Hash initialization function. */ - CURLX_FUNCTION_CAST(HMAC_hinit_func, my_sha256_init), - /* Hash update function. */ - CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_sha256_update), - /* Hash computation end function. */ - CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_sha256_final), - /* Size of hash context structure. */ - sizeof(my_sha256_ctx), - /* Maximum key length. */ - 64, - /* Result size. */ - 32 - } +const struct HMAC_params Curl_HMAC_SHA256 = { + my_sha256_init, /* Hash initialization function. */ + my_sha256_update, /* Hash update function. */ + my_sha256_final, /* Hash computation end function. */ + sizeof(my_sha256_ctx), /* Size of hash context structure. */ + 64, /* Maximum key length. */ + 32 /* Result size. */ }; diff --git a/lib/share.c b/lib/share.c index ed4a2093872efb..6cdba18ec40a05 100644 --- a/lib/share.c +++ b/lib/share.c @@ -38,7 +38,7 @@ #include "curl_memory.h" #include "memdebug.h" -struct Curl_share * +CURLSH * curl_share_init(void) { struct Curl_share *share = calloc(1, sizeof(struct Curl_share)); @@ -53,7 +53,7 @@ curl_share_init(void) #undef curl_share_setopt CURLSHcode -curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) +curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) { va_list param; int type; @@ -61,6 +61,7 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) curl_unlock_function unlockfunc; void *ptr; CURLSHcode res = CURLSHE_OK; + struct Curl_share *share = sh; if(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; @@ -214,8 +215,9 @@ curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) } CURLSHcode -curl_share_cleanup(struct Curl_share *share) +curl_share_cleanup(CURLSH *sh) { + struct Curl_share *share = sh; if(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; diff --git a/lib/smb.c b/lib/smb.c index b51b2cc5b9abc1..a242fc5c2a2857 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -28,7 +28,9 @@ #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) #ifdef _WIN32 -#define getpid GetCurrentProcessId +#define Curl_getpid() ((unsigned int)GetCurrentProcessId()) +#else +#define Curl_getpid() ((unsigned int)getpid()) #endif #include "smb.h" @@ -44,7 +46,8 @@ #include "escape.h" #include "curl_endian.h" -/* The last #include files should be: */ +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" @@ -316,20 +319,6 @@ const struct Curl_handler Curl_handler_smbs = { #define CLIENTNAME "curl" #define SERVICENAME "?????" -/* Append a string to an SMB message */ -#define MSGCAT(str) \ - do { \ - strcpy(p, (str)); \ - p += strlen(str); \ - } while(0) - -/* Append a null-terminated string to an SMB message */ -#define MSGCATNULL(str) \ - do { \ - strcpy(p, (str)); \ - p += strlen(str) + 1; \ - } while(0) - /* SMB is mostly little endian */ #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ defined(__OS400__) @@ -559,7 +548,7 @@ static void smb_format_message(struct Curl_easy *data, struct smb_header *h, h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME); h->uid = smb_swap16(smbc->uid); h->tid = smb_swap16(req->tid); - pid = (unsigned int)getpid(); + pid = Curl_getpid(); h->pid_high = smb_swap16((unsigned short)(pid >> 16)); h->pid = smb_swap16((unsigned short) pid); } @@ -644,7 +633,7 @@ static CURLcode smb_send_setup(struct Curl_easy *data) const size_t byte_count = sizeof(lm) + sizeof(nt) + strlen(smbc->user) + strlen(smbc->domain) + - strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */ + strlen(CURL_OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */ if(byte_count > sizeof(msg.bytes)) return CURLE_FILESIZE_EXCEEDED; @@ -667,10 +656,13 @@ static CURLcode smb_send_setup(struct Curl_easy *data) p += sizeof(lm); memcpy(p, nt, sizeof(nt)); p += sizeof(nt); - MSGCATNULL(smbc->user); - MSGCATNULL(smbc->domain); - MSGCATNULL(OS); - MSGCATNULL(CLIENTNAME); + p += msnprintf(p, byte_count - sizeof(nt) - sizeof(lm), + "%s%c" /* user */ + "%s%c" /* domain */ + "%s%c" /* OS */ + "%s", /* client name */ + smbc->user, 0, smbc->domain, 0, CURL_OS, 0, CLIENTNAME); + p++; /* count the final null termination */ DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); msg.byte_count = smb_swap16((unsigned short)byte_count); @@ -694,11 +686,13 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data) msg.word_count = SMB_WC_TREE_CONNECT_ANDX; msg.andx.command = SMB_COM_NO_ANDX_COMMAND; msg.pw_len = 0; - MSGCAT("\\\\"); - MSGCAT(conn->host.name); - MSGCAT("\\"); - MSGCATNULL(smbc->share); - MSGCATNULL(SERVICENAME); /* Match any type of service */ + + p += msnprintf(p, byte_count, + "\\\\%s\\" /* hostname */ + "%s%c" /* share */ + "%s", /* service */ + conn->host.name, smbc->share, 0, SERVICENAME); + p++; /* count the final null termination */ DEBUGASSERT(byte_count == (size_t)(p - msg.bytes)); msg.byte_count = smb_swap16((unsigned short)byte_count); diff --git a/lib/smtp.c b/lib/smtp.c index 3c5893284bd979..d854d364f8555f 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -1100,12 +1100,11 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data, (void)instate; /* no use for this yet */ - is_smtp_err = (smtpcode/100 != 2) ? TRUE : FALSE; + is_smtp_err = (smtpcode/100 != 2); /* If there is multiple RCPT TO to be issued, it is possible to ignore errors and proceed with only the valid addresses. */ - is_smtp_blocking_err = - (is_smtp_err && !data->set.mail_rcpt_allowfails) ? TRUE : FALSE; + is_smtp_blocking_err = (is_smtp_err && !data->set.mail_rcpt_allowfails); if(is_smtp_err) { /* Remembering the last failure which we can report if all "RCPT TO" have @@ -1296,7 +1295,7 @@ static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done) } result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE); - *done = (smtpc->state == SMTP_STOP) ? TRUE : FALSE; + *done = (smtpc->state == SMTP_STOP); return result; } diff --git a/lib/socketpair.h b/lib/socketpair.h index 3044f1122ee4fa..ed69c5af826aa7 100644 --- a/lib/socketpair.h +++ b/lib/socketpair.h @@ -27,14 +27,14 @@ #include "curl_setup.h" #if defined(HAVE_EVENTFD) && \ - defined(__x86_64__) && \ - defined(__aarch64__) && \ - defined(__ia64__) && \ - defined(__ppc64__) && \ - defined(__mips64) && \ - defined(__sparc64__) && \ - defined(__riscv_64e) && \ - defined(__s390x__) + (defined(__x86_64__) || \ + defined(__aarch64__) || \ + defined(__ia64__) || \ + defined(__ppc64__) || \ + defined(__mips64) || \ + defined(__sparc64__) || \ + defined(__riscv_64e) || \ + defined(__s390x__)) /* Use eventfd only with 64-bit CPU architectures because eventfd has a * stringent rule of requiring the 8-byte buffer when calling read(2) and diff --git a/lib/socks.c b/lib/socks.c index 733d047eb229c5..d16a30b90adbef 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -286,7 +286,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf, { struct connectdata *conn = cf->conn; const bool protocol4a = - (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A) ? TRUE : FALSE; + (conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A); unsigned char *socksreq = sx->buffer; CURLcode result; CURLproxycode presult; @@ -583,7 +583,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf, CURLcode result; CURLproxycode presult; bool socks5_resolve_local = - (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5) ? TRUE : FALSE; + (conn->socks_proxy.proxytype == CURLPROXY_SOCKS5); const size_t hostname_len = strlen(sx->hostname); size_t len = 0; const unsigned char auth = data->set.socks5auth; diff --git a/lib/speedcheck.h b/lib/speedcheck.h index bff2f32b774023..8b116f15289d5a 100644 --- a/lib/speedcheck.h +++ b/lib/speedcheck.h @@ -27,7 +27,7 @@ #include "curl_setup.h" #include "timeval.h" - +struct Curl_easy; void Curl_speedinit(struct Curl_easy *data); CURLcode Curl_speedcheck(struct Curl_easy *data, struct curltime now); diff --git a/lib/splay.c b/lib/splay.c index 5e27b08a6cd9e1..3f2bae0238743a 100644 --- a/lib/splay.c +++ b/lib/splay.c @@ -113,9 +113,9 @@ struct Curl_tree *Curl_splayinsert(struct curltime i, t = Curl_splay(i, t); DEBUGASSERT(t); if(compare(i, t->key) == 0) { - /* There already exists a node in the tree with the very same key. Build - a doubly-linked circular list of nodes. We add the new 'node' struct - to the end of this list. */ + /* There already exists a node in the tree with the same key. Build a + doubly-linked circular list of nodes. We add the new 'node' struct to + the end of this list. */ node->key = KEY_NOTUSED; /* we set the key in the sub node to NOTUSED to quickly identify this node as a subnode */ @@ -199,7 +199,7 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i, } -/* Deletes the very node we point out from the tree if it is there. Stores a +/* Deletes the node we point out from the tree if it is there. Stores a * pointer to the new resulting tree in 'newroot'. * * Returns zero on success and non-zero on errors! diff --git a/lib/strerror.c b/lib/strerror.c index 76a8ba2d7ee63e..6b67a905880e00 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -151,9 +151,6 @@ curl_easy_strerror(CURLcode error) case CURLE_RANGE_ERROR: return "Requested range was not delivered by the server"; - case CURLE_HTTP_POST_ERROR: - return "Internal problem setting up the POST"; - case CURLE_SSL_CONNECT_ERROR: return "SSL connect error"; @@ -169,9 +166,6 @@ curl_easy_strerror(CURLcode error) case CURLE_LDAP_SEARCH_FAILED: return "LDAP: search failed"; - case CURLE_FUNCTION_NOT_FOUND: - return "A required function in the library was not found"; - case CURLE_ABORTED_BY_CALLBACK: return "Operation was aborted by an application callback"; @@ -330,7 +324,9 @@ curl_easy_strerror(CURLcode error) case CURLE_OBSOLETE24: case CURLE_OBSOLETE29: case CURLE_OBSOLETE32: + case CURLE_OBSOLETE34: case CURLE_OBSOLETE40: + case CURLE_OBSOLETE41: case CURLE_OBSOLETE44: case CURLE_OBSOLETE46: case CURLE_OBSOLETE50: @@ -348,9 +344,9 @@ curl_easy_strerror(CURLcode error) * By using gcc -Wall -Werror, you cannot forget. * * A table would not have the same benefit. Most compilers will generate - * code very similar to a table in any case, so there is little performance - * gain from a table. Something is broken for the user's application, - * anyways, so does it matter how fast it _does not_ work? + * code similar to a table in any case, so there is little performance gain + * from a table. Something is broken for the user's application, anyways, so + * does it matter how fast it _does not_ work? * * The line number for the error will be near this comment, which is why it * is here, and not at the start of the switch. diff --git a/lib/strtok.h b/lib/strtok.h index 321cba23262003..9b4d06275fb707 100644 --- a/lib/strtok.h +++ b/lib/strtok.h @@ -26,11 +26,11 @@ #include "curl_setup.h" #include -#ifndef HAVE_STRTOK_R -char *Curl_strtok_r(char *s, const char *delim, char **last); -#define strtok_r Curl_strtok_r -#else +#ifdef HAVE_STRTOK_R #include +#define Curl_strtok_r strtok_r +#else +char *Curl_strtok_r(char *s, const char *delim, char **last); #endif #endif /* HEADER_CURL_STRTOK_H */ diff --git a/lib/telnet.c b/lib/telnet.c index 3e6abad92ec1b6..64d552d1592b18 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -154,8 +154,8 @@ struct TELNET { int himq[256]; int him_preferred[256]; int subnegotiation[256]; - char subopt_ttype[32]; /* Set with suboption TTYPE */ - char subopt_xdisploc[128]; /* Set with suboption XDISPLOC */ + char *subopt_ttype; /* Set with suboption TTYPE */ + char *subopt_xdisploc; /* Set with suboption XDISPLOC */ unsigned short subopt_wsx; /* Set with suboption NAWS */ unsigned short subopt_wsy; /* Set with suboption NAWS */ TelnetReceive telrcv_state; @@ -831,12 +831,9 @@ static CURLcode check_telnet_options(struct Curl_easy *data) case 5: /* Terminal type */ if(strncasecompare(option, "TTYPE", 5)) { - size_t l = strlen(arg); - if(l < sizeof(tn->subopt_ttype)) { - strcpy(tn->subopt_ttype, arg); - tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; - break; - } + tn->subopt_ttype = arg; + tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; + break; } result = CURLE_UNKNOWN_OPTION; break; @@ -844,12 +841,9 @@ static CURLcode check_telnet_options(struct Curl_easy *data) case 8: /* Display variable */ if(strncasecompare(option, "XDISPLOC", 8)) { - size_t l = strlen(arg); - if(l < sizeof(tn->subopt_xdisploc)) { - strcpy(tn->subopt_xdisploc, arg); - tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; - break; - } + tn->subopt_xdisploc = arg; + tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; + break; } result = CURLE_UNKNOWN_OPTION; break; diff --git a/lib/tftp.c b/lib/tftp.c index 7fb22a373865d5..e92e5127a56bde 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -205,7 +205,7 @@ static CURLcode tftp_set_timeouts(struct tftp_state_data *state) { time_t maxtime, timeout; timediff_t timeout_ms; - bool start = (state->state == TFTP_STATE_START) ? TRUE : FALSE; + bool start = (state->state == TFTP_STATE_START); /* Compute drop-dead time */ timeout_ms = Curl_timeleft(state->data, NULL, start); @@ -482,11 +482,9 @@ static CURLcode tftp_send_first(struct tftp_state_data *state, if(!data->set.tftp_no_options) { char buf[64]; /* add tsize option */ - if(data->state.upload && (data->state.infilesize != -1)) - msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, - data->state.infilesize); - else - strcpy(buf, "0"); /* the destination is large enough */ + msnprintf(buf, sizeof(buf), "%" FMT_OFF_T, + data->state.upload && (data->state.infilesize != -1) ? + data->state.infilesize : 0); result = tftp_option_add(state, &sbytes, (char *)state->spacket.data + sbytes, @@ -1000,7 +998,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) return CURLE_OUT_OF_MEMORY; } - /* we do not keep TFTP connections up basically because there is none or very + /* we do not keep TFTP connections up basically because there is none or * little gain for UDP */ connclose(conn, "TFTP"); @@ -1099,24 +1097,20 @@ static int tftp_getsock(struct Curl_easy *data, **********************************************************/ static CURLcode tftp_receive_packet(struct Curl_easy *data) { - struct Curl_sockaddr_storage fromaddr; curl_socklen_t fromlen; CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct tftp_state_data *state = conn->proto.tftpc; /* Receive the packet */ - fromlen = sizeof(fromaddr); + fromlen = sizeof(state->remote_addr); state->rbytes = (int)recvfrom(state->sockfd, (void *)state->rpacket.data, (RECV_TYPE_ARG3)state->blksize + 4, 0, - (struct sockaddr *)&fromaddr, + (struct sockaddr *)&state->remote_addr, &fromlen); - if(state->remote_addrlen == 0) { - memcpy(&state->remote_addr, &fromaddr, fromlen); - state->remote_addrlen = fromlen; - } + state->remote_addrlen = fromlen; /* Sanity check packet length */ if(state->rbytes < 4) { @@ -1238,7 +1232,7 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) result = tftp_state_machine(state, event); if(result) return result; - *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; + *done = (state->state == TFTP_STATE_FIN); if(*done) /* Tell curl we are done */ Curl_xfer_setup_nop(data); @@ -1261,7 +1255,7 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done) result = tftp_state_machine(state, state->event); if(result) return result; - *done = (state->state == TFTP_STATE_FIN) ? TRUE : FALSE; + *done = (state->state == TFTP_STATE_FIN); if(*done) /* Tell curl we are done */ Curl_xfer_setup_nop(data); diff --git a/lib/transfer.c b/lib/transfer.c index 2f003529fd8938..d7d3d16f3eeed4 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -79,7 +79,6 @@ #include "http2.h" #include "mime.h" #include "strcase.h" -#include "urlapi-int.h" #include "hsts.h" #include "setopt.h" #include "headers.h" @@ -205,10 +204,10 @@ CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done) * @param err error code in case of -1 return * @return number of bytes read or -1 for error */ -static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data, - char *buf, size_t blen, - bool eos_reliable, - CURLcode *err) +static ssize_t xfer_recv_resp(struct Curl_easy *data, + char *buf, size_t blen, + bool eos_reliable, + CURLcode *err) { ssize_t nread; @@ -303,8 +302,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, bytestoread = (size_t)data->set.max_recv_speed; } - nread = Curl_xfer_recv_resp(data, buf, bytestoread, - is_multiplex, &result); + nread = xfer_recv_resp(data, buf, bytestoread, is_multiplex, &result); if(nread < 0) { if(CURLE_AGAIN != result) goto out; /* real error */ @@ -680,6 +678,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return CURLE_OUT_OF_MEMORY; } + if(data->set.str[STRING_USERNAME] || + data->set.str[STRING_PASSWORD]) + data->state.creds_from = CREDS_OPTION; if(!result) result = Curl_setstropt(&data->state.aptr.user, data->set.str[STRING_USERNAME]); @@ -700,298 +701,6 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) return result; } -/* - * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string - * as given by the remote server and set up the new URL to request. - * - * This function DOES NOT FREE the given url. - */ -CURLcode Curl_follow(struct Curl_easy *data, - char *newurl, /* the Location: string */ - followtype type) /* see transfer.h */ -{ -#ifdef CURL_DISABLE_HTTP - (void)data; - (void)newurl; - (void)type; - /* Location: following will not happen when HTTP is disabled */ - return CURLE_TOO_MANY_REDIRECTS; -#else - - /* Location: redirect */ - bool disallowport = FALSE; - bool reachedmax = FALSE; - CURLUcode uc; - - DEBUGASSERT(type != FOLLOW_NONE); - - if(type != FOLLOW_FAKE) - data->state.requests++; /* count all real follows */ - if(type == FOLLOW_REDIR) { - if((data->set.maxredirs != -1) && - (data->state.followlocation >= data->set.maxredirs)) { - reachedmax = TRUE; - type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected - to URL */ - } - else { - data->state.followlocation++; /* count redirect-followings, including - auth reloads */ - - if(data->set.http_auto_referer) { - CURLU *u; - char *referer = NULL; - - /* We are asked to automatically set the previous URL as the referer - when we get the next URL. We pick the ->url field, which may or may - not be 100% correct */ - - if(data->state.referer_alloc) { - Curl_safefree(data->state.referer); - data->state.referer_alloc = FALSE; - } - - /* Make a copy of the URL without credentials and fragment */ - u = curl_url(); - if(!u) - return CURLE_OUT_OF_MEMORY; - - uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_USER, NULL, 0); - if(!uc) - uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0); - if(!uc) - uc = curl_url_get(u, CURLUPART_URL, &referer, 0); - - curl_url_cleanup(u); - - if(uc || !referer) - return CURLE_OUT_OF_MEMORY; - - data->state.referer = referer; - data->state.referer_alloc = TRUE; /* yes, free this later */ - } - } - } - - if((type != FOLLOW_RETRY) && - (data->req.httpcode != 401) && (data->req.httpcode != 407) && - Curl_is_absolute_url(newurl, NULL, 0, FALSE)) { - /* If this is not redirect due to a 401 or 407 response and an absolute - URL: do not allow a custom port number */ - disallowport = TRUE; - } - - DEBUGASSERT(data->state.uh); - uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int) - ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : - ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) | - CURLU_ALLOW_SPACE | - (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); - if(uc) { - if(type != FOLLOW_FAKE) { - failf(data, "The redirect target URL could not be parsed: %s", - curl_url_strerror(uc)); - return Curl_uc_to_curlcode(uc); - } - - /* the URL could not be parsed for some reason, but since this is FAKE - mode, just duplicate the field as-is */ - newurl = strdup(newurl); - if(!newurl) - return CURLE_OUT_OF_MEMORY; - } - else { - uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0); - if(uc) - return Curl_uc_to_curlcode(uc); - - /* Clear auth if this redirects to a different port number or protocol, - unless permitted */ - if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) { - char *portnum; - int port; - bool clear = FALSE; - - if(data->set.use_port && data->state.allow_port) - /* a custom port is used */ - port = (int)data->set.use_port; - else { - uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, - CURLU_DEFAULT_PORT); - if(uc) { - free(newurl); - return Curl_uc_to_curlcode(uc); - } - port = atoi(portnum); - free(portnum); - } - if(port != data->info.conn_remote_port) { - infof(data, "Clear auth, redirects to port from %u to %u", - data->info.conn_remote_port, port); - clear = TRUE; - } - else { - char *scheme; - const struct Curl_handler *p; - uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - free(newurl); - return Curl_uc_to_curlcode(uc); - } - - p = Curl_get_scheme_handler(scheme); - if(p && (p->protocol != data->info.conn_protocol)) { - infof(data, "Clear auth, redirects scheme from %s to %s", - data->info.conn_scheme, scheme); - clear = TRUE; - } - free(scheme); - } - if(clear) { - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); - } - } - } - - if(type == FOLLOW_FAKE) { - /* we are only figuring out the new URL if we would have followed locations - but now we are done so we can get out! */ - data->info.wouldredirect = newurl; - - if(reachedmax) { - failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs); - return CURLE_TOO_MANY_REDIRECTS; - } - return CURLE_OK; - } - - if(disallowport) - data->state.allow_port = FALSE; - - if(data->state.url_alloc) - Curl_safefree(data->state.url); - - data->state.url = newurl; - data->state.url_alloc = TRUE; - Curl_req_soft_reset(&data->req, data); - infof(data, "Issue another request to this URL: '%s'", data->state.url); - - /* - * We get here when the HTTP code is 300-399 (and 401). We need to perform - * differently based on exactly what return code there was. - * - * News from 7.10.6: we can also get here on a 401 or 407, in case we act on - * an HTTP (proxy-) authentication scheme other than Basic. - */ - switch(data->info.httpcode) { - /* 401 - Act on a WWW-Authenticate, we keep on moving and do the - Authorization: XXXX header in the HTTP request code snippet */ - /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the - Proxy-Authorization: XXXX header in the HTTP request code snippet */ - /* 300 - Multiple Choices */ - /* 306 - Not used */ - /* 307 - Temporary Redirect */ - default: /* for all above (and the unknown ones) */ - /* Some codes are explicitly mentioned since I have checked RFC2616 and - * they seem to be OK to POST to. - */ - break; - case 301: /* Moved Permanently */ - /* (quote from RFC7231, section 6.4.2) - * - * Note: For historical reasons, a user agent MAY change the request - * method from POST to GET for the subsequent request. If this - * behavior is undesired, the 307 (Temporary Redirect) status code - * can be used instead. - * - * ---- - * - * Many webservers expect this, so these servers often answers to a POST - * request with an error page. To be sure that libcurl gets the page that - * most user agents would get, libcurl has to force GET. - * - * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and - * can be overridden with CURLOPT_POSTREDIR. - */ - if((data->state.httpreq == HTTPREQ_POST - || data->state.httpreq == HTTPREQ_POST_FORM - || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_301)) { - infof(data, "Switch from POST to GET"); - data->state.httpreq = HTTPREQ_GET; - Curl_creader_set_rewind(data, FALSE); - } - break; - case 302: /* Found */ - /* (quote from RFC7231, section 6.4.3) - * - * Note: For historical reasons, a user agent MAY change the request - * method from POST to GET for the subsequent request. If this - * behavior is undesired, the 307 (Temporary Redirect) status code - * can be used instead. - * - * ---- - * - * Many webservers expect this, so these servers often answers to a POST - * request with an error page. To be sure that libcurl gets the page that - * most user agents would get, libcurl has to force GET. - * - * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and - * can be overridden with CURLOPT_POSTREDIR. - */ - if((data->state.httpreq == HTTPREQ_POST - || data->state.httpreq == HTTPREQ_POST_FORM - || data->state.httpreq == HTTPREQ_POST_MIME) - && !(data->set.keep_post & CURL_REDIR_POST_302)) { - infof(data, "Switch from POST to GET"); - data->state.httpreq = HTTPREQ_GET; - Curl_creader_set_rewind(data, FALSE); - } - break; - - case 303: /* See Other */ - /* 'See Other' location is not the resource but a substitute for the - * resource. In this case we switch the method to GET/HEAD, unless the - * method is POST and the user specified to keep it as POST. - * https://github.com/curl/curl/issues/5237#issuecomment-614641049 - */ - if(data->state.httpreq != HTTPREQ_GET && - ((data->state.httpreq != HTTPREQ_POST && - data->state.httpreq != HTTPREQ_POST_FORM && - data->state.httpreq != HTTPREQ_POST_MIME) || - !(data->set.keep_post & CURL_REDIR_POST_303))) { - data->state.httpreq = HTTPREQ_GET; - infof(data, "Switch to %s", - data->req.no_body ? "HEAD" : "GET"); - } - break; - case 304: /* Not Modified */ - /* 304 means we did a conditional request and it was "Not modified". - * We should not get any Location: header in this response! - */ - break; - case 305: /* Use Proxy */ - /* (quote from RFC2616, section 10.3.6): - * "The requested resource MUST be accessed through the proxy given - * by the Location field. The Location field gives the URI of the - * proxy. The recipient is expected to repeat this single request - * via the proxy. 305 responses MUST only be generated by origin - * servers." - */ - break; - } - Curl_pgrsTime(data, TIMER_REDIRECT); - Curl_pgrsResetTransferSizes(data); - - return CURLE_OK; -#endif /* CURL_DISABLE_HTTP */ -} - /* Returns CURLE_OK *and* sets '*url' if a request retry is wanted. NOTE: that the *url is malloc()ed. */ @@ -1067,8 +776,8 @@ static void xfer_setup( int sockindex, /* socket index to read from or -1 */ curl_off_t size, /* -1 if unknown at this point */ bool getheader, /* TRUE if header parsing is wanted */ - int writesockindex, /* socket index to write to, it may very well be - the same we read from. -1 disables */ + int writesockindex, /* socket index to write to, it may be the same we + read from. -1 disables */ bool shutdown, /* shutdown connection at transfer end. Only * supported when sending OR receiving. */ bool shutdown_err_ignore /* errors during shutdown do not fail the @@ -1091,7 +800,7 @@ static void xfer_setup( conn->sock[sockindex]; conn->writesockfd = conn->sockfd; if(want_send) - /* special and very HTTP-specific */ + /* special and HTTP-specific */ writesockindex = FIRSTSOCKET; } else { diff --git a/lib/transfer.h b/lib/transfer.h index 87a5a389ff89ea..8c9b88c17850e7 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -42,8 +42,6 @@ typedef enum { FOLLOW_REDIR /* a full true redirect */ } followtype; -CURLcode Curl_follow(struct Curl_easy *data, char *newurl, - followtype type); CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp); int Curl_single_getsock(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *socks); diff --git a/lib/url.c b/lib/url.c index a59cb0e349d392..436edd891e1b22 100644 --- a/lib/url.c +++ b/lib/url.c @@ -338,6 +338,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_wildcard_dtor(&data->wildcard); Curl_freeset(data); Curl_headers_cleanup(data); + Curl_netrc_cleanup(&data->state.netrc); free(data); return CURLE_OK; } @@ -505,10 +506,10 @@ CURLcode Curl_open(struct Curl_easy **curl) CURLcode result; struct Curl_easy *data; - /* Very simple start-up: alloc the struct, init it with zeroes and return */ + /* simple start-up: alloc the struct, init it with zeroes and return */ data = calloc(1, sizeof(struct Curl_easy)); if(!data) { - /* this is a very serious error */ + /* this is a serious error */ DEBUGF(fprintf(stderr, "Error: calloc of Curl_easy failed\n")); return CURLE_OUT_OF_MEMORY; } @@ -545,6 +546,7 @@ CURLcode Curl_open(struct Curl_easy **curl) #ifndef CURL_DISABLE_HTTP Curl_llist_init(&data->state.httphdrs, NULL); #endif + Curl_netrc_init(&data->state.netrc); } if(result) { @@ -649,13 +651,13 @@ bool Curl_on_disconnect(struct Curl_easy *data, } /* - * Curl_xfer_may_multiplex() + * xfer_may_multiplex() * * Return a TRUE, iff the transfer can be done over an (appropriate) * multiplexed connection. */ -static bool Curl_xfer_may_multiplex(const struct Curl_easy *data, - const struct connectdata *conn) +static bool xfer_may_multiplex(const struct Curl_easy *data, + const struct connectdata *conn) { /* If an HTTP protocol and multiplexing is enabled */ if((conn->handler->protocol & PROTO_FAMILY_HTTP) && @@ -878,16 +880,16 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) } if(needle->localdev || needle->localport) { - /* If we are bound to a specific local end (IP+port), we must not - reuse a random other one, although if we did not ask for a - particular one we can reuse one that was bound. + /* If we are bound to a specific local end (IP+port), we must not reuse a + random other one, although if we did not ask for a particular one we + can reuse one that was bound. This comparison is a bit rough and too strict. Since the input - parameters can be specified in numerous ways and still end up the - same it would take a lot of processing to make it really accurate. - Instead, this matching will assume that reuses of bound connections - will most likely also reuse the exact same binding parameters and - missing out a few edge cases should not hurt anyone very much. + parameters can be specified in numerous ways and still end up the same + it would take a lot of processing to make it really accurate. Instead, + this matching will assume that reuses of bound connections will most + likely also reuse the exact same binding parameters and missing out a + few edge cases should not hurt anyone much. */ if((conn->localport != needle->localport) || (conn->localportrange != needle->localportrange) || @@ -1246,7 +1248,7 @@ ConnectionExists(struct Curl_easy *data, memset(&match, 0, sizeof(match)); match.data = data; match.needle = needle; - match.may_multiplex = Curl_xfer_may_multiplex(data, needle); + match.may_multiplex = xfer_may_multiplex(data, needle); #ifdef USE_NTLM match.want_ntlm_http = ((data->state.authhost.want & CURLAUTH_NTLM) && @@ -1341,22 +1343,19 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) /* note that these two proxy bits are now just 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]) ? TRUE : FALSE; + *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))) ? - TRUE : FALSE; - conn->bits.socksproxy = (conn->bits.proxy && - !conn->bits.httpproxy) ? TRUE : FALSE; + 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) ? TRUE : FALSE; + conn->bits.proxy_user_passwd = !!data->state.aptr.proxyuser; conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; #endif /* CURL_DISABLE_PROXY */ @@ -1858,10 +1857,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return result; /* - * username and password set with their own options override the - * credentials possibly set in the URL. + * username and password set with their own options override the credentials + * possibly set in the URL, but netrc does not. */ - if(!data->set.str[STRING_PASSWORD]) { + 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; @@ -1874,12 +1873,13 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, 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(!data->set.str[STRING_USERNAME]) { + 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 */ @@ -1893,13 +1893,10 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return result; conn->user = decoded; result = Curl_setstropt(&data->state.aptr.user, decoded); + data->state.creds_from = CREDS_URL; } else if(uc != CURLUE_NO_USER) return Curl_uc_to_curlcode(uc); - else if(data->state.aptr.passwd) { - /* no user was set but a password, set a blank user */ - result = Curl_setstropt(&data->state.aptr.user, ""); - } if(result) return result; } @@ -1961,11 +1958,11 @@ static CURLcode setup_range(struct Curl_easy *data) else s->range = strdup(data->set.str[STRING_SET_RANGE]); - s->rangestringalloc = (s->range) ? TRUE : FALSE; - if(!s->range) return CURLE_OUT_OF_MEMORY; + s->rangestringalloc = TRUE; + /* tell ourselves to fetch this range */ s->use_range = TRUE; /* enable range download */ } @@ -2006,8 +2003,8 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, } if(conn->primary.remote_port < 0) - /* we check for -1 here since if proxy was detected already, this - was very likely already set to the proxy port */ + /* we check for -1 here since if proxy was detected already, this was + likely already set to the proxy port */ conn->primary.remote_port = p->defport; /* Now create the destination name */ @@ -2654,6 +2651,17 @@ static CURLcode parse_remote_port(struct Curl_easy *data, return CURLE_OK; } +static bool str_has_ctrl(const char *input) +{ + const unsigned char *str = (const unsigned char *)input; + while(*str) { + if(*str < 0x20) + return TRUE; + str++; + } + return FALSE; +} + /* * Override the login details from the URL with that in the CURLOPT_USERPWD * option or a .netrc file, if applicable. @@ -2683,30 +2691,42 @@ static CURLcode override_login(struct Curl_easy *data, int ret; bool url_provided = FALSE; - if(data->state.aptr.user) { - /* there was a username in the URL. Use the URL decoded version */ + 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; } - ret = Curl_parsenetrc(conn->host.name, - userp, passwdp, - data->set.str[STRING_NETRC_FILE]); - if(ret > 0) { - infof(data, "Couldn't 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 < 0) { - failf(data, ".netrc parser error"); - return CURLE_READ_ERROR; - } - else { - /* 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(!*passwdp) { + ret = Curl_parsenetrc(&data->state.netrc, conn->host.name, + userp, passwdp, + data->set.str[STRING_NETRC_FILE]); + if(ret > 0) { + infof(data, "Couldn't 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 < 0) { + failf(data, ".netrc parser error"); + return CURLE_READ_ERROR; + } + else { + if(!(conn->handler->flags&PROTOPT_USERPWDCTRL)) { + /* if the protocol can't 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) { Curl_safefree(conn->user); @@ -2731,6 +2751,7 @@ static CURLcode override_login(struct Curl_easy *data, result = Curl_setstropt(&data->state.aptr.user, *userp); if(result) return result; + data->state.creds_from = CREDS_NETRC; } } if(data->state.aptr.user) { @@ -2748,6 +2769,7 @@ static CURLcode override_login(struct Curl_easy *data, 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, diff --git a/lib/urldata.h b/lib/urldata.h index 028ac0aff54f14..704fb7a1d22136 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -163,6 +163,7 @@ typedef unsigned int curl_prot_t; #include "dynbuf.h" #include "dynhds.h" #include "request.h" +#include "netrc.h" /* return the count of bytes sent, or -1 on error */ typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ @@ -324,6 +325,7 @@ struct ssl_config_data { char *key_passwd; /* plain text private key password */ BIT(certinfo); /* gather lots of certificate info */ BIT(falsestart); + BIT(earlydata); /* use tls1.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 */ @@ -346,6 +348,7 @@ struct Curl_ssl_session { char *name; /* hostname for which this ID was used */ char *conn_to_host; /* hostname for the connection (may be NULL) */ const char *scheme; /* protocol scheme used */ + char *alpn; /* APLN TLS negotiated protocol string */ void *sessionid; /* as returned from the SSL layer */ size_t idsize; /* if known, otherwise 0 */ Curl_ssl_sessionid_dtor *sessionid_free; /* free `sessionid` callback */ @@ -868,9 +871,8 @@ struct connectdata { /**** curl_get() phase fields */ curl_socket_t sockfd; /* socket to read from or CURL_SOCKET_BAD */ - curl_socket_t writesockfd; /* socket to write to, it may very - well be the same we read from. - CURL_SOCKET_BAD disables */ + curl_socket_t writesockfd; /* socket to write to, it may be the same we read + from. CURL_SOCKET_BAD disables */ #ifdef HAVE_GSSAPI BIT(sec_complete); /* if Kerberos is enabled for this connection */ @@ -1069,6 +1071,7 @@ struct Progress { struct pgrs_dir dl; curl_off_t current_speed; /* uses the currently fastest transfer */ + curl_off_t earlydata_sent; int width; /* screen width at download start */ int flags; /* see progress.h */ @@ -1202,6 +1205,11 @@ 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 */ @@ -1310,6 +1318,10 @@ struct UrlState { struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */ #endif +#ifndef CURL_DISABLE_NETRC + struct store_netrc netrc; +#endif + /* Dynamically allocated strings, MUST be freed before this struct is killed. */ struct dynamically_allocated_data { @@ -1336,7 +1348,6 @@ struct UrlState { char *proxypasswd; #endif } aptr; - unsigned char httpwant; /* when non-zero, a specific HTTP version requested to be used in the library's request(s) */ unsigned char httpversion; /* the lowest HTTP version*10 reported by any @@ -1346,6 +1357,9 @@ struct UrlState { unsigned char select_bits; /* != 0 -> bitmask of socket events for this transfer overriding anything the socket may report */ + 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! */ @@ -1881,14 +1895,12 @@ struct Curl_easy { /* First a simple identifier to easier detect if a user mix up this easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ unsigned int magic; - /* once an easy handle is tied to a connection pool - a non-negative number to distinguish this transfer from - other using the same pool. For easier tracking - in log output. - This may wrap around after LONG_MAX to 0 again, so it - has no uniqueness guarantee for very large processings. - Note: it has no uniqueness either IFF more than one connection pool - is used by the libcurl application. */ + /* once an easy handle is tied to a connection pool a non-negative number to + distinguish this transfer from other using the same pool. For easier + tracking in log output. This may wrap around after LONG_MAX to 0 again, + so it has no uniqueness guarantee for large processings. Note: it has no + uniqueness either IFF more than one connection pool is used by the + libcurl application. */ curl_off_t id; /* once an easy handle is added to a multi, either explicitly by the * libcurl application or implicitly during `curl_easy_perform()`, diff --git a/lib/vauth/cram.c b/lib/vauth/cram.c index f8bdd5458de448..c51c7285b478cf 100644 --- a/lib/vauth/cram.c +++ b/lib/vauth/cram.c @@ -67,7 +67,7 @@ CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg, char *response; /* Compute the digest using the password as the key */ - ctxt = Curl_HMAC_init(Curl_HMAC_MD5, + ctxt = Curl_HMAC_init(&Curl_HMAC_MD5, (const unsigned char *) passwdp, curlx_uztoui(strlen(passwdp))); if(!ctxt) diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index 4fc5b1c28f8e29..0cc3da898f1a10 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -227,12 +227,12 @@ static CURLcode auth_digest_get_qop_values(const char *options, int *value) *value = 0; /* Tokenise the list of qop values. Use a temporary clone of the buffer since - strtok_r() ruins it. */ + Curl_strtok_r() ruins it. */ tmp = strdup(options); if(!tmp) return CURLE_OUT_OF_MEMORY; - token = strtok_r(tmp, ",", &tok_buf); + token = Curl_strtok_r(tmp, ",", &tok_buf); while(token) { if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) *value |= DIGEST_QOP_VALUE_AUTH; @@ -241,7 +241,7 @@ static CURLcode auth_digest_get_qop_values(const char *options, int *value) else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF)) *value |= DIGEST_QOP_VALUE_AUTH_CONF; - token = strtok_r(NULL, ",", &tok_buf); + token = Curl_strtok_r(NULL, ",", &tok_buf); } free(tmp); @@ -388,7 +388,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return result; /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -402,7 +402,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, curlx_uztoui(strlen(passwdp))); Curl_MD5_final(ctxt, digest); - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -425,7 +425,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Calculate H(A2) */ - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) { free(spn); @@ -443,7 +443,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]); /* Now calculate the response hash */ - ctxt = Curl_MD5_init(Curl_DIGEST_MD5); + ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) { free(spn); @@ -553,12 +553,12 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, else if(strcasecompare(value, "qop")) { char *tok_buf = NULL; /* Tokenize the list and choose auth if possible, use a temporary - clone of the buffer since strtok_r() ruins it */ + clone of the buffer since Curl_strtok_r() ruins it */ tmp = strdup(content); if(!tmp) return CURLE_OUT_OF_MEMORY; - token = strtok_r(tmp, ",", &tok_buf); + token = Curl_strtok_r(tmp, ",", &tok_buf); while(token) { /* Pass additional spaces here */ while(*token && ISBLANK(*token)) @@ -569,7 +569,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) { foundAuthInt = TRUE; } - token = strtok_r(NULL, ",", &tok_buf); + token = Curl_strtok_r(NULL, ",", &tok_buf); } free(tmp); diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index e686b0aaf90edb..2ae6fb30c93c11 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -69,7 +69,7 @@ bool Curl_auth_is_digest_supported(void) Curl_pSecFn->FreeContextBuffer(SecurityPackage); } - return (status == SEC_E_OK ? TRUE : FALSE); + return (status == SEC_E_OK); } /* diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c index b168a27ad88205..4af0bd1e134d86 100644 --- a/lib/vauth/krb5_sspi.c +++ b/lib/vauth/krb5_sspi.c @@ -64,7 +64,7 @@ bool Curl_auth_is_gssapi_supported(void) Curl_pSecFn->FreeContextBuffer(SecurityPackage); } - return (status == SEC_E_OK ? TRUE : FALSE); + return (status == SEC_E_OK); } /* diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c index 0e3d98aa9c96a1..f8f6aea0e9e25c 100644 --- a/lib/vauth/ntlm.c +++ b/lib/vauth/ntlm.c @@ -485,7 +485,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, unsigned char ntresp[24]; /* fixed-size */ unsigned char *ptr_ntresp = &ntresp[0]; unsigned char *ntlmv2resp = NULL; - bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE) ? TRUE : FALSE; + bool unicode = (ntlm->flags & NTLMFLAG_NEGOTIATE_UNICODE); /* 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"; diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c index 55ec8201d82689..a2e5bc102696be 100644 --- a/lib/vauth/ntlm_sspi.c +++ b/lib/vauth/ntlm_sspi.c @@ -63,7 +63,7 @@ bool Curl_auth_is_ntlm_supported(void) Curl_pSecFn->FreeContextBuffer(SecurityPackage); } - return (status == SEC_E_OK ? TRUE : FALSE); + return (status == SEC_E_OK); } /* diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 38b26ab90ca80d..7a27c298fc11a9 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -67,7 +67,7 @@ bool Curl_auth_is_spnego_supported(void) } - return (status == SEC_E_OK ? TRUE : FALSE); + return (status == SEC_E_OK); } /* diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index ace43c47d1ebaf..a7e7e17f3b236d 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -134,8 +134,7 @@ bool Curl_auth_user_contains_domain(const char *user) /* Check we have a domain name or UPN present */ char *p = strpbrk(user, "\\/@"); - valid = (p != NULL && p > user && p < user + strlen(user) - 1 ? TRUE : - FALSE); + valid = (p != NULL && p > user && p < user + strlen(user) - 1); } #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) else diff --git a/lib/version.c b/lib/version.c index 3f0f0ae5034a95..033349b511f8a4 100644 --- a/lib/version.c +++ b/lib/version.c @@ -63,13 +63,13 @@ #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) /* Ignore -Wvla warnings in brotli headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvla" #endif #include -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif #endif @@ -93,20 +93,71 @@ static void brotli_version(char *buf, size_t bufsz) unsigned int major = brotli_version >> 24; unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12; unsigned int patch = brotli_version & 0x00000FFF; - (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch); + (void)msnprintf(buf, bufsz, "brotli/%u.%u.%u", major, minor, patch); } #endif #ifdef HAVE_ZSTD static void zstd_version(char *buf, size_t bufsz) { - unsigned long zstd_version = (unsigned long)ZSTD_versionNumber(); - unsigned int major = (unsigned int)(zstd_version / (100 * 100)); - unsigned int minor = (unsigned int)((zstd_version - - (major * 100 * 100)) / 100); - unsigned int patch = (unsigned int)(zstd_version - - (major * 100 * 100) - (minor * 100)); - (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch); + unsigned int version = ZSTD_versionNumber(); + unsigned int major = version / (100 * 100); + unsigned int minor = (version - (major * 100 * 100)) / 100; + unsigned int patch = version - (major * 100 * 100) - (minor * 100); + (void)msnprintf(buf, bufsz, "zstd/%u.%u.%u", major, minor, patch); +} +#endif + +#ifdef USE_OPENLDAP +static void oldap_version(char *buf, size_t bufsz) +{ + LDAPAPIInfo api; + api.ldapai_info_version = LDAP_API_INFO_VERSION; + + if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) { + unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); + unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); + unsigned int minor = + (((unsigned int)api.ldapai_vendor_version - major * 10000) + - patch) / 100; + msnprintf(buf, bufsz, "%s/%u.%u.%u", + api.ldapai_vendor_name, major, minor, patch); + ldap_memfree(api.ldapai_vendor_name); + ber_memvfree((void **)api.ldapai_extensions); + } + else + msnprintf(buf, bufsz, "OpenLDAP"); +} +#endif + +#ifdef USE_LIBPSL +static void psl_version(char *buf, size_t bufsz) +{ +#if defined(PSL_VERSION_MAJOR) && (PSL_VERSION_MAJOR > 0 || \ + PSL_VERSION_MINOR >= 11) + int num = psl_check_version_number(0); + msnprintf(buf, bufsz, "libpsl/%d.%d.%d", + num >> 16, (num >> 8) & 0xff, num & 0xff); +#else + msnprintf(buf, bufsz, "libpsl/%s", psl_get_version()); +#endif +} +#endif + +#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN) +#define USE_IDN +#endif + +#ifdef USE_IDN +static void idn_version(char *buf, size_t bufsz) +{ +#ifdef USE_LIBIDN2 + msnprintf(buf, bufsz, "libidn2/%s", idn2_check_version(NULL)); +#elif defined(USE_WIN32_IDN) + msnprintf(buf, bufsz, "WinIDN"); +#elif defined(USE_APPLE_IDN) + msnprintf(buf, bufsz, "AppleIDN"); +#endif } #endif @@ -130,34 +181,34 @@ char *curl_version(void) char ssl_version[200]; #endif #ifdef HAVE_LIBZ - char z_version[40]; + char z_version[30]; #endif #ifdef HAVE_BROTLI - char br_version[40] = "brotli/"; + char br_version[30]; #endif #ifdef HAVE_ZSTD - char zst_version[40] = "zstd/"; + char zstd_ver[30]; #endif #ifdef USE_ARES - char cares_version[40]; + char cares_version[30]; #endif -#if defined(USE_LIBIDN2) - char idn_version[40]; +#ifdef USE_IDN + char idn_ver[30]; #endif #ifdef USE_LIBPSL - char psl_version[40]; + char psl_ver[30]; #endif #ifdef USE_SSH - char ssh_version[40]; + char ssh_version[30]; #endif #ifdef USE_NGHTTP2 - char h2_version[40]; + char h2_version[30]; #endif #ifdef USE_HTTP3 - char h3_version[40]; + char h3_version[30]; #endif #ifdef USE_LIBRTMP - char rtmp_version[40]; + char rtmp_version[30]; #endif #ifdef USE_HYPER char hyper_buf[30]; @@ -190,43 +241,26 @@ char *curl_version(void) src[i++] = z_version; #endif #ifdef HAVE_BROTLI - brotli_version(&br_version[7], sizeof(br_version) - 7); + brotli_version(br_version, sizeof(br_version)); src[i++] = br_version; #endif #ifdef HAVE_ZSTD - zstd_version(&zst_version[5], sizeof(zst_version) - 5); - src[i++] = zst_version; + zstd_version(zstd_ver, sizeof(zstd_ver)); + src[i++] = zstd_ver; #endif #ifdef USE_ARES msnprintf(cares_version, sizeof(cares_version), "c-ares/%s", ares_version(NULL)); src[i++] = cares_version; #endif -#ifdef USE_LIBIDN2 - msnprintf(idn_version, sizeof(idn_version), - "libidn2/%s", idn2_check_version(NULL)); - src[i++] = idn_version; -#elif defined(USE_WIN32_IDN) - src[i++] = (char *)"WinIDN"; -#elif defined(USE_APPLE_IDN) - src[i++] = (char *)"AppleIDN"; +#ifdef USE_IDN + idn_version(idn_ver, sizeof(idn_ver)); + src[i++] = idn_ver; #endif - #ifdef USE_LIBPSL - { -#if defined(PSL_VERSION_MAJOR) && (PSL_VERSION_MAJOR > 0 || \ - PSL_VERSION_MINOR >= 11) - int num = psl_check_version_number(0); - msnprintf(psl_version, sizeof(psl_version), "libpsl/%d.%d.%d", - num >> 16, (num >> 8) & 0xff, num & 0xff); -#else - msnprintf(psl_version, sizeof(psl_version), "libpsl/%s", - psl_get_version()); -#endif - src[i++] = psl_version; - } + psl_version(psl_ver, sizeof(psl_ver)); + src[i++] = psl_ver; #endif - #ifdef USE_SSH Curl_ssh_version(ssh_version, sizeof(ssh_version)); src[i++] = ssh_version; @@ -253,23 +287,8 @@ char *curl_version(void) src[i++] = gsasl_buf; #endif #ifdef USE_OPENLDAP - { - LDAPAPIInfo api; - api.ldapai_info_version = LDAP_API_INFO_VERSION; - - if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) == LDAP_OPT_SUCCESS) { - unsigned int patch = (unsigned int)(api.ldapai_vendor_version % 100); - unsigned int major = (unsigned int)(api.ldapai_vendor_version / 10000); - unsigned int minor = - (((unsigned int)api.ldapai_vendor_version - major * 10000) - - patch) / 100; - msnprintf(ldap_buf, sizeof(ldap_buf), "%s/%u.%u.%u", - api.ldapai_vendor_name, major, minor, patch); - src[i++] = ldap_buf; - ldap_memfree(api.ldapai_vendor_name); - ber_memvfree((void **)api.ldapai_extensions); - } - } + oldap_version(ldap_buf, sizeof(ldap_buf)); + src[i++] = ldap_buf; #endif DEBUGASSERT(i <= VERSION_PARTS); @@ -532,7 +551,7 @@ static const struct feat features_table[] = { #ifdef HAVE_ZSTD FEATURE("zstd", NULL, CURL_VERSION_ZSTD), #endif - {NULL, NULL, 0} + {NULL, NULL, 0} }; static const char *feature_names[sizeof(features_table) / @@ -543,7 +562,7 @@ static curl_version_info_data version_info = { CURLVERSION_NOW, LIBCURL_VERSION, LIBCURL_VERSION_NUM, - OS, /* as found by configure or set by hand at build-time */ + CURL_OS, /* as found by configure or set by hand at build-time */ 0, /* features bitmask is built at runtime */ NULL, /* ssl_version */ 0, /* ssl_version_num, this is kept at zero */ diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index ef93d98af39445..37009abdc16004 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -41,6 +41,7 @@ #include "vtls/gtls.h" #elif defined(USE_WOLFSSL) #include +#include "vtls/wolfssl.h" #endif #include "urldata.h" @@ -342,7 +343,6 @@ struct pkt_io_ctx { struct Curl_cfilter *cf; struct Curl_easy *data; ngtcp2_tstamp ts; - size_t pkt_count; ngtcp2_path_storage ps; }; @@ -362,7 +362,6 @@ static void pktx_init(struct pkt_io_ctx *pktx, { pktx->cf = cf; pktx->data = data; - pktx->pkt_count = 0; ngtcp2_path_storage_zero(&pktx->ps); pktx_update_time(pktx, cf); } @@ -429,7 +428,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx, s->initial_ts = pktx->ts; s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT; s->max_window = 100 * ctx->max_stream_window; - s->max_stream_window = ctx->max_stream_window; + s->max_stream_window = 10 * ctx->max_stream_window; t->initial_max_data = 10 * ctx->max_stream_window; t->initial_max_stream_data_bidi_local = ctx->max_stream_window; @@ -1639,7 +1638,6 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, ngtcp2_path path; int rv; - ++pktx->pkt_count; ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, (socklen_t)ctx->q.local_addrlen); ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, @@ -1668,31 +1666,18 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct pkt_io_ctx local_pktx; - size_t pkts_chunk = 128, i; CURLcode result = CURLE_OK; if(!pktx) { pktx_init(&local_pktx, cf, data); pktx = &local_pktx; } - else { - pktx_update_time(pktx, cf); - } result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); if(result) return result; - for(i = 0; i < 4; ++i) { - if(i) - pktx_update_time(pktx, cf); - pktx->pkt_count = 0; - result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk, - recv_pkt, pktx); - if(result || !pktx->pkt_count) /* error or got nothing */ - break; - } - return result; + return vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, pktx); } /** @@ -2153,12 +2138,63 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) } #endif /* USE_OPENSSL */ +#ifdef USE_GNUTLS +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) { + CURL_TRC_CF(data, cf, "handshake: %s message type %d", + incoming ? "incoming" : "outgoing", htype); + } + switch(htype) { + case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { + (void)Curl_gtls_update_session_id(cf, data, session, &ctx->peer, "h3"); + 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) { + (void)wssl_cache_session(cf, data, &ctx->peer, session); + } + } + return 0; +} +#endif /* USE_WOLFSSL */ + static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, struct Curl_easy *data, void *user_data) { struct curl_tls_ctx *ctx = user_data; - (void)cf; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + #ifdef USE_OPENSSL #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx) @@ -2172,25 +2208,37 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf, return CURLE_FAILED_INIT; } #endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ - /* 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); + if(ssl_config->primary.cache_session) { + /* 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(ssl_config->primary.cache_session) { + 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.ctx) != 0) { failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed"); return CURLE_FAILED_INIT; } + if(ssl_config->primary.cache_session) { + /* Register to get notified when a new session is received */ + wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ctx, wssl_quic_new_session_cb); + } #endif return CURLE_OK; } @@ -2270,8 +2318,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, 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); -#else +#elif defined(USE_WOLFSSL) ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle); +#else + #error "ngtcp2 TLS backend not defined" #endif ngtcp2_ccerr_default(&ctx->last_error); diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index 882314659a1a7d..580e5707a8deb6 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -76,11 +76,11 @@ static void keylog_callback(const WOLFSSL *ssl, const char *line) } #endif -static CURLcode Curl_wssl_init_ctx(struct curl_tls_ctx *ctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - Curl_vquic_tls_ctx_setup *cb_setup, - void *cb_user_data) +static CURLcode wssl_init_ctx(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + Curl_vquic_tls_ctx_setup *cb_setup, + void *cb_user_data) { struct ssl_primary_config *conn_config; CURLcode result = CURLE_FAILED_INIT; @@ -194,13 +194,15 @@ static CURLcode Curl_wssl_init_ctx(struct curl_tls_ctx *ctx, /** SSL callbacks ***/ -static CURLcode Curl_wssl_init_ssl(struct curl_tls_ctx *ctx, - struct Curl_easy *data, - struct ssl_peer *peer, - const char *alpn, size_t alpn_len, - void *user_data) +static CURLcode wssl_init_ssl(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const char *alpn, size_t alpn_len, + void *user_data) { - (void)data; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + DEBUGASSERT(!ctx->wssl.handle); DEBUGASSERT(ctx->wssl.ctx); ctx->wssl.handle = wolfSSL_new(ctx->wssl.ctx); @@ -218,6 +220,10 @@ static CURLcode Curl_wssl_init_ssl(struct curl_tls_ctx *ctx, peer->sni, (unsigned short)strlen(peer->sni)); } + if(ssl_config->primary.cache_session) { + (void)wssl_setup_session(cf, data, &ctx->wssl, peer); + } + return CURLE_OK; } #endif /* defined(USE_WOLFSSL) */ @@ -240,14 +246,14 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, #elif defined(USE_GNUTLS) (void)result; return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, - (const unsigned char *)alpn, alpn_len, + (const unsigned char *)alpn, alpn_len, NULL, cb_setup, cb_user_data, ssl_user_data); #elif defined(USE_WOLFSSL) - result = Curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); + result = wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data); if(result) return result; - return Curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data); + return wssl_init_ssl(ctx, cf, data, peer, alpn, alpn_len, ssl_user_data); #else #error "no TLS lib in used, should not happen" return CURLE_FAILED_INIT; diff --git a/lib/vquic/vquic-tls.h b/lib/vquic/vquic-tls.h index 0ec74bfbae3acb..969acad41efcab 100644 --- a/lib/vquic/vquic-tls.h +++ b/lib/vquic/vquic-tls.h @@ -64,7 +64,7 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, * @param alpn the ALPN string in protocol format ((len+bytes+)+), * may be NULL * @param alpn_len the overall number of bytes in `alpn` - * @param cb_setup optional callback for very early TLS config + * @param cb_setup optional callback for early TLS config Âą @param cb_user_data user_data param for callback * @param ssl_user_data optional pointer to set in TLS application context */ diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 715328bc9e5158..20ff6a640b7ec4 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -39,6 +39,7 @@ #include "curl_ngtcp2.h" #include "curl_osslq.h" #include "curl_quiche.h" +#include "multiif.h" #include "rand.h" #include "vquic.h" #include "vquic_int.h" @@ -141,8 +142,8 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, /* Only set this, when we need it. macOS, for example, * does not seem to like a msg_control of length 0. */ msg.msg_control = msg_ctrl; - assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t))); - msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(int))); + msg.msg_controllen = CMSG_SPACE(sizeof(int)); cm = CMSG_FIRSTHDR(&msg); cm->cmsg_level = SOL_UDP; cm->cmsg_type = UDP_SEGMENT; @@ -321,20 +322,20 @@ CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, } #if defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG) -static size_t msghdr_get_udp_gro(struct msghdr *msg) +static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg) { int gso_size = 0; #if defined(__linux__) && defined(UDP_GRO) struct cmsghdr *cmsg; /* Workaround musl CMSG_NXTHDR issue */ -#ifndef __GLIBC__ +#if defined(__clang__) && !defined(__GLIBC__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wsign-compare" #pragma clang diagnostic ignored "-Wcast-align" #endif for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { -#ifndef __GLIBC__ +#if defined(__clang__) && !defined(__GLIBC__) #pragma clang diagnostic pop #endif if(cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) { @@ -357,21 +358,28 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, size_t max_pkts, vquic_recv_pkt_cb *recv_cb, void *userp) { -#define MMSG_NUM 64 +#define MMSG_NUM 16 struct iovec msg_iov[MMSG_NUM]; struct mmsghdr mmsg[MMSG_NUM]; - uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(uint16_t))]; - uint8_t bufs[MMSG_NUM][2*1024]; + uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))]; struct sockaddr_storage remote_addr[MMSG_NUM]; - size_t total_nread, pkts; + size_t total_nread = 0, pkts; int mcount, i, n; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; size_t gso_size; size_t pktlen; size_t offset, to; + char *sockbuf = NULL; + uint8_t (*bufs)[64*1024] = NULL; DEBUGASSERT(max_pkts > 0); + result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * sizeof(bufs[0]), + &sockbuf); + if(result) + goto out; + bufs = (uint8_t (*)[64*1024])sockbuf; + pkts = 0; total_nread = 0; while(pkts < max_pkts) { @@ -384,8 +392,8 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, mmsg[i].msg_hdr.msg_iovlen = 1; mmsg[i].msg_hdr.msg_name = &remote_addr[i]; mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]); - mmsg[i].msg_hdr.msg_control = &msg_ctrl[i]; - mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + mmsg[i].msg_hdr.msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))]; + mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(int)); } while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 && @@ -415,7 +423,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, for(i = 0; i < mcount; ++i) { total_nread += mmsg[i].msg_len; - gso_size = msghdr_get_udp_gro(&mmsg[i].msg_hdr); + gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr); if(gso_size == 0) { gso_size = mmsg[i].msg_len; } @@ -443,6 +451,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, if(total_nread || result) CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", pkts, total_nread, result); + Curl_multi_xfer_sockbuf_release(data, sockbuf); return result; } @@ -461,7 +470,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, ssize_t nread; char errstr[STRERROR_LEN]; CURLcode result = CURLE_OK; - uint8_t msg_ctrl[CMSG_SPACE(sizeof(uint16_t))]; + uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))]; size_t gso_size; size_t pktlen; size_t offset, to; @@ -503,7 +512,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, total_nread += (size_t)nread; - gso_size = msghdr_get_udp_gro(&msg); + gso_size = vquic_msghdr_get_udp_gro(&msg); if(gso_size == 0) { gso_size = (size_t)nread; } diff --git a/lib/curl_path.c b/lib/vssh/curl_path.c similarity index 100% rename from lib/curl_path.c rename to lib/vssh/curl_path.c index 144f8803d3a668..61452a42527c2d 100644 --- a/lib/curl_path.c +++ b/lib/vssh/curl_path.c @@ -26,9 +26,9 @@ #if defined(USE_SSH) +#include "curl_path.h" #include #include "curl_memory.h" -#include "curl_path.h" #include "escape.h" #include "memdebug.h" diff --git a/lib/curl_path.h b/lib/vssh/curl_path.h similarity index 83% rename from lib/curl_path.h rename to lib/vssh/curl_path.h index 6fdb2fddfff473..8e984174d79c5c 100644 --- a/lib/curl_path.h +++ b/lib/vssh/curl_path.h @@ -28,19 +28,6 @@ #include #include "urldata.h" -#ifdef _WIN32 -# undef PATH_MAX -# define PATH_MAX MAX_PATH -# ifndef R_OK -# define R_OK 4 -# endif -#endif - -#ifndef PATH_MAX -#define PATH_MAX 1024 /* just an extra precaution since there are systems that - have their definition hidden well */ -#endif - CURLcode Curl_getworkingpath(struct Curl_easy *data, char *homedir, char **path); diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 2dbe861c37c959..eeef5ca01d6e30 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -695,7 +695,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) case SSH_S_STARTUP: rc = ssh_connect(sshc->ssh_session); - myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE); + myssh_block2waitfor(conn, (rc == SSH_AGAIN)); if(rc == SSH_AGAIN) { DEBUGF(infof(data, "ssh_connect -> EAGAIN")); break; @@ -2085,7 +2085,7 @@ static CURLcode myssh_multi_statemach(struct Curl_easy *data, implementation */ CURLcode result = myssh_statemach_act(data, &block); - *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; + *done = (sshc->state == SSH_STOP); myssh_block2waitfor(conn, block); return result; @@ -2146,7 +2146,7 @@ static CURLcode myssh_setup_connection(struct Curl_easy *data, data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); if(!ssh) return CURLE_OUT_OF_MEMORY; - Curl_dyn_init(&sshc->readdir_buf, PATH_MAX * 2); + Curl_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2); return CURLE_OK; } @@ -2191,7 +2191,14 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) return CURLE_FAILED_INIT; } - rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name); + if(conn->bits.ipv6_ip) { + char ipv6[MAX_IPADR_LEN]; + msnprintf(ipv6, sizeof(ipv6), "[%s]", conn->host.name); + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, ipv6); + } + else + rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name); + if(rc != SSH_OK) { failf(data, "Could not set remote host"); return CURLE_FAILED_INIT; @@ -2415,7 +2422,7 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex, /* The following code is misleading, mostly added as wishful thinking * that libssh at some point will implement non-blocking ssh_scp_write/read. * Currently rc can only be number of bytes read or SSH_ERROR. */ - myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE); + myssh_block2waitfor(conn, (rc == SSH_AGAIN)); if(rc == SSH_AGAIN) { *err = CURLE_AGAIN; @@ -2447,7 +2454,7 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex, * that libssh at some point will implement non-blocking ssh_scp_write/read. * Currently rc can only be SSH_OK or SSH_ERROR. */ - myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE); + myssh_block2waitfor(conn, (nread == SSH_AGAIN)); if(nread == SSH_AGAIN) { *err = CURLE_AGAIN; nread = -1; @@ -2614,7 +2621,7 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, mem, (uint32_t)len, (uint32_t)conn->proto.sshc.sftp_file_index); - myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE); + myssh_block2waitfor(conn, (nread == SSH_AGAIN)); if(nread == SSH_AGAIN) { *err = CURLE_AGAIN; diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index c088201387a09a..e19ffefe8b5f16 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -389,7 +389,7 @@ static void state(struct Curl_easy *data, sshstate nowstate) #ifdef HAVE_LIBSSH2_KNOWNHOST_API -static int sshkeycallback(struct Curl_easy *easy, +static int sshkeycallback(CURL *easy, const struct curl_khkey *knownkey, /* known */ const struct curl_khkey *foundkey, /* found */ enum curl_khmatch match, @@ -959,25 +959,744 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data) return result; } +static CURLcode sftp_quote(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + const char *cp; + CURLcode result = CURLE_OK; + + /* + * Support some of the "FTP" commands + * + * 'sshc->quote_item' is already verified to be non-NULL before it + * switched to this state. + */ + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(strcasecompare("pwd", cmd)) { + /* output debug output if that is requested */ + char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path); + if(!tmp) + return CURLE_OUT_OF_MEMORY; + Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4); + Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); + + /* this sends an FTP-like "header" to the header callback so that the + current directory can be read very similar to how it is read when + using ordinary FTP. */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(!result) + state(data, SSH_SFTP_NEXT_QUOTE); + return result; + } + + /* + * the arguments following the command must be separated from the + * command with a space so we can check for it unconditionally + */ + cp = strchr(cmd, ' '); + if(!cp) { + failf(data, "Syntax error command '%s', missing parameter", cmd); + return result; + } + + /* + * also, every command takes at least one argument so we get that + * first argument right now + */ + result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + if(result) { + if(result != CURLE_OUT_OF_MEMORY) + failf(data, "Syntax error: Bad first parameter to '%s'", cmd); + return result; + } + + /* + * SFTP is a binary protocol, so we do not send text commands to the server. + * Instead, we scan for commands used by OpenSSH's sftp program and call the + * appropriate libssh2 functions. + */ + if(strncasecompare(cmd, "chgrp ", 6) || + strncasecompare(cmd, "chmod ", 6) || + strncasecompare(cmd, "chown ", 6) || + strncasecompare(cmd, "atime ", 6) || + strncasecompare(cmd, "mtime ", 6)) { + /* attribute change */ + + /* sshc->quote_path1 contains the mode to set */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result != CURLE_OUT_OF_MEMORY) + failf(data, "Syntax error in %s: Bad second parameter", cmd); + Curl_safefree(sshc->quote_path1); + return result; + } + memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); + state(data, SSH_SFTP_QUOTE_STAT); + return result; + } + if(strncasecompare(cmd, "ln ", 3) || + strncasecompare(cmd, "symlink ", 8)) { + /* symbolic linking */ + /* sshc->quote_path1 is the source */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result != CURLE_OUT_OF_MEMORY) + failf(data, "Syntax error in ln/symlink: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + return result; + } + state(data, SSH_SFTP_QUOTE_SYMLINK); + return result; + } + else if(strncasecompare(cmd, "mkdir ", 6)) { + /* create dir */ + state(data, SSH_SFTP_QUOTE_MKDIR); + return result; + } + else if(strncasecompare(cmd, "rename ", 7)) { + /* rename file */ + /* first param is the source path */ + /* second param is the dest. path */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result != CURLE_OUT_OF_MEMORY) + failf(data, "Syntax error in rename: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + return result; + } + state(data, SSH_SFTP_QUOTE_RENAME); + return result; + } + else if(strncasecompare(cmd, "rmdir ", 6)) { + /* delete dir */ + state(data, SSH_SFTP_QUOTE_RMDIR); + return result; + } + else if(strncasecompare(cmd, "rm ", 3)) { + state(data, SSH_SFTP_QUOTE_UNLINK); + return result; + } +#ifdef HAS_STATVFS_SUPPORT + else if(strncasecompare(cmd, "statvfs ", 8)) { + state(data, SSH_SFTP_QUOTE_STATVFS); + return result; + } +#endif + + failf(data, "Unknown SFTP command"); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + return CURLE_QUOTE_ERROR; +} + +static CURLcode +sftp_upload_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *blockp) +{ + unsigned long flags; + + /* + * NOTE!!! libssh2 requires that the destination path is a full path + * that includes the destination file and name OR ends in a "/" + * If this is not done the destination file will be named the + * same name as the last directory in the path. + */ + + if(data->state.resume_from) { + LIBSSH2_SFTP_ATTRIBUTES attrs; + if(data->state.resume_from < 0) { + int rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_SFTP_STAT, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + *blockp = TRUE; + return CURLE_OK; + } + if(rc) { + data->state.resume_from = 0; + } + else { + curl_off_t size = attrs.filesize; + if(size < 0) { + failf(data, "Bad file size (%" FMT_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + data->state.resume_from = attrs.filesize; + } + } + } + + if(data->set.remote_append) + /* Try to open for append, but create if nonexisting */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND; + else if(data->state.resume_from > 0) + /* If we have restart position then open for append */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND; + else + /* Clear file before writing (normal behavior) */ + flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC; + + sshc->sftp_handle = + libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + flags, (long)data->set.new_file_perms, + LIBSSH2_SFTP_OPENFILE); + + if(!sshc->sftp_handle) { + unsigned long sftperr; + int rc = libssh2_session_last_errno(sshc->ssh_session); + + if(LIBSSH2_ERROR_EAGAIN == rc) { + *blockp = TRUE; + return CURLE_OK; + } + + if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc) + /* only when there was an SFTP protocol error can we extract + the sftp error! */ + sftperr = libssh2_sftp_last_error(sshc->sftp_session); + else + sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */ + + if(sshc->secondCreateDirs) { + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH; + failf(data, "Creating the dir/file failed: %s", + sftp_libssh2_strerror(sftperr)); + return CURLE_OK; + } + if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) || + (sftperr == LIBSSH2_FX_FAILURE) || + (sftperr == LIBSSH2_FX_NO_SUCH_PATH)) && + (data->set.ftp_create_missing_dirs && + (strlen(sshp->path) > 1))) { + /* try to create the path remotely */ + sshc->secondCreateDirs = 1; + state(data, SSH_SFTP_CREATE_DIRS_INIT); + return CURLE_OK; + } + state(data, SSH_SFTP_CLOSE); + sshc->actualcode = sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH; + if(!sshc->actualcode) { + /* Sometimes, for some reason libssh2_sftp_last_error() returns zero + even though libssh2_sftp_open() failed previously! We need to + work around that! */ + sshc->actualcode = CURLE_SSH; + sftperr = LIBSSH2_FX_OK; + } + failf(data, "Upload failed: %s (%lu/%d)", + sftperr != LIBSSH2_FX_OK ? + sftp_libssh2_strerror(sftperr) : "ssh error", + sftperr, rc); + return sshc->actualcode; + } + + /* If we have a restart point then we need to seek to the correct + position. */ + if(data->state.resume_from > 0) { + int seekerr = CURL_SEEKFUNC_OK; + /* Let's read off the proper amount of bytes from the input. */ + if(data->set.seek_func) { + Curl_set_in_callback(data, TRUE); + seekerr = data->set.seek_func(data->set.seek_client, + data->state.resume_from, SEEK_SET); + Curl_set_in_callback(data, FALSE); + } + + if(seekerr != CURL_SEEKFUNC_OK) { + curl_off_t passed = 0; + + if(seekerr != CURL_SEEKFUNC_CANTSEEK) { + failf(data, "Could not seek stream"); + return CURLE_FTP_COULDNT_USE_REST; + } + /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ + do { + char scratch[4*1024]; + size_t readthisamountnow = + (data->state.resume_from - passed > + (curl_off_t)sizeof(scratch)) ? + sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed); + + size_t actuallyread; + Curl_set_in_callback(data, TRUE); + actuallyread = data->state.fread_func(scratch, 1, + readthisamountnow, + data->state.in); + Curl_set_in_callback(data, FALSE); + + passed += actuallyread; + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { + /* this checks for greater-than only to make sure that the + CURL_READFUNC_ABORT return code still aborts */ + failf(data, "Failed to read data"); + return CURLE_FTP_COULDNT_USE_REST; + } + } while(passed < data->state.resume_from); + } + + /* now, decrease the size of the read */ + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + + SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); + } + if(data->state.infilesize > 0) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); + } + /* upload data */ + Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + data->conn->sockfd = data->conn->writesockfd; + + /* store this original bitmask setup to use later on if we cannot + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh2 sftp send function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_OUT; + + /* since we do not really wait for anything at this point, we want the + state machine to move on as soon as possible so we set a very short + timeout here */ + Curl_expire(data, 0, EXPIRE_RUN_NOW); + + state(data, SSH_STOP); + return CURLE_OK; +} + +static CURLcode +sftp_pkey_init(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + /* + * Check the supported auth types in the order I feel is most secure + * with the requested type of authentication + */ + sshc->authed = FALSE; + + if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && + (strstr(sshc->authlist, "publickey") != NULL)) { + bool out_of_memory = FALSE; + + sshc->rsa_pub = sshc->rsa = NULL; + + if(data->set.str[STRING_SSH_PRIVATE_KEY]) + sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + else { + /* To ponder about: should really the lib be messing about with the + HOME environment variable etc? */ + char *home = curl_getenv("HOME"); + struct_stat sbuf; + + /* If no private key file is specified, try some common paths. */ + if(home) { + /* Try ~/.ssh first. */ + sshc->rsa = aprintf("%s/.ssh/id_rsa", home); + if(!sshc->rsa) + out_of_memory = TRUE; + else if(stat(sshc->rsa, &sbuf)) { + Curl_safefree(sshc->rsa); + sshc->rsa = aprintf("%s/.ssh/id_dsa", home); + if(!sshc->rsa) + out_of_memory = TRUE; + else if(stat(sshc->rsa, &sbuf)) { + Curl_safefree(sshc->rsa); + } + } + free(home); + } + if(!out_of_memory && !sshc->rsa) { + /* Nothing found; try the current dir. */ + sshc->rsa = strdup("id_rsa"); + if(sshc->rsa && stat(sshc->rsa, &sbuf)) { + Curl_safefree(sshc->rsa); + sshc->rsa = strdup("id_dsa"); + if(sshc->rsa && stat(sshc->rsa, &sbuf)) { + Curl_safefree(sshc->rsa); + /* Out of guesses. Set to the empty string to avoid + * surprising info messages. */ + sshc->rsa = strdup(""); + } + } + } + } + + /* + * Unless the user explicitly specifies a public key file, let + * libssh2 extract the public key from the private key file. + * This is done by simply passing sshc->rsa_pub = NULL. + */ + if(data->set.str[STRING_SSH_PUBLIC_KEY] + /* treat empty string the same way as NULL */ + && data->set.str[STRING_SSH_PUBLIC_KEY][0]) { + sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); + if(!sshc->rsa_pub) + out_of_memory = TRUE; + } + + if(out_of_memory || !sshc->rsa) { + Curl_safefree(sshc->rsa); + Curl_safefree(sshc->rsa_pub); + state(data, SSH_SESSION_FREE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + return CURLE_OUT_OF_MEMORY; + } + + sshc->passphrase = data->set.ssl.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); + + state(data, SSH_AUTH_PKEY); + } + else { + state(data, SSH_AUTH_PASS_INIT); + } + return CURLE_OK; +} + +static CURLcode +sftp_quote_stat(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *blockp) +{ + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any aborts or + cancels etc. It will cause libcurl to act as if the command is + successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(!strncasecompare(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. + */ + int rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, + curlx_uztoui(strlen(sshc->quote_path2)), + LIBSSH2_SFTP_STAT, + &sshp->quote_attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + *blockp = TRUE; + return CURLE_OK; + } + if(rc && !sshc->acceptfail) { /* get those attributes */ + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); + failf(data, "Attempt to get SFTP stats failed: %s", + sftp_libssh2_strerror(sftperr)); + goto fail; + } + } + + /* Now set the new attributes... */ + if(strncasecompare(cmd, "chgrp", 5)) { + sshp->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10); + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; + if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + failf(data, "Syntax error: chgrp gid not a number"); + goto fail; + } + } + else if(strncasecompare(cmd, "chmod", 5)) { + sshp->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8); + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; + /* permissions are octal */ + if(sshp->quote_attrs.permissions == 0 && + !ISDIGIT(sshc->quote_path1[0])) { + failf(data, "Syntax error: chmod permissions not a number"); + goto fail; + } + } + else if(strncasecompare(cmd, "chown", 5)) { + sshp->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10); + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; + if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + failf(data, "Syntax error: chown uid not a number"); + goto fail; + } + } + else if(strncasecompare(cmd, "atime", 5) || + strncasecompare(cmd, "mtime", 5)) { + time_t date = Curl_getdate_capped(sshc->quote_path1); + bool fail = FALSE; + + if(date == -1) { + failf(data, "incorrect date format for %.*s", 5, cmd); + fail = TRUE; + } +#if SIZEOF_TIME_T > SIZEOF_LONG + if(date > 0xffffffff) { + /* if 'long' cannot old >32-bit, this date cannot be sent */ + failf(data, "date overflow"); + fail = TRUE; + } +#endif + if(fail) + goto fail; + if(strncasecompare(cmd, "atime", 5)) + sshp->quote_attrs.atime = (unsigned long)date; + else /* mtime */ + sshp->quote_attrs.mtime = (unsigned long)date; + + sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME; + } + + /* Now send the completed structure... */ + state(data, SSH_SFTP_QUOTE_SETSTAT); + return CURLE_OK; +fail: + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + return CURLE_QUOTE_ERROR; +} + +static CURLcode +sftp_download_stat(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *blockp) +{ + LIBSSH2_SFTP_ATTRIBUTES attrs; + int rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_SFTP_STAT, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + *blockp = TRUE; + return CURLE_OK; + } + if(rc || + !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) || + (attrs.filesize == 0)) { + /* + * libssh2_sftp_open() did not return an error, so maybe the server + * just does not support stat() + * OR the server does not return a file size with a stat() + * OR file size is 0 + */ + data->req.size = -1; + data->req.maxdownload = -1; + Curl_pgrsSetDownloadSize(data, -1); + } + else { + curl_off_t size = attrs.filesize; + + if(size < 0) { + failf(data, "Bad file size (%" FMT_OFF_T ")", size); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(data->state.use_range) { + curl_off_t from, to; + char *ptr; + char *ptr2; + CURLofft to_t; + CURLofft from_t; + + from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); + if(from_t == CURL_OFFT_FLOW) + return CURLE_RANGE_ERROR; + while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) + ptr++; + to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); + if(to_t == CURL_OFFT_FLOW) + return CURLE_RANGE_ERROR; + if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ + || (to >= size)) { + to = size - 1; + } + if(from_t) { + /* from is relative to end of file */ + from = size - to; + to = size - 1; + } + if(from > size) { + failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" + FMT_OFF_T ")", from, (curl_off_t)attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + if(from > to) { + from = to; + size = 0; + } + else { + if((to - from) == CURL_OFF_T_MAX) + return CURLE_RANGE_ERROR; + size = to - from + 1; + } + + SFTP_SEEK(sshc->sftp_handle, from); + } + data->req.size = size; + data->req.maxdownload = size; + Curl_pgrsSetDownloadSize(data, size); + } + + /* We can resume if we can seek to the resume position */ + if(data->state.resume_from) { + if(data->state.resume_from < 0) { + /* We are supposed to download the last abs(from) bytes */ + if((curl_off_t)attrs.filesize < -data->state.resume_from) { + failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" + FMT_OFF_T ")", + data->state.resume_from, (curl_off_t)attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + /* download from where? */ + data->state.resume_from += attrs.filesize; + } + else { + if((curl_off_t)attrs.filesize < data->state.resume_from) { + failf(data, "Offset (%" FMT_OFF_T + ") was beyond file size (%" FMT_OFF_T ")", + data->state.resume_from, (curl_off_t)attrs.filesize); + return CURLE_BAD_DOWNLOAD_RESUME; + } + } + /* Now store the number of bytes we are expected to download */ + data->req.size = attrs.filesize - data->state.resume_from; + data->req.maxdownload = attrs.filesize - data->state.resume_from; + Curl_pgrsSetDownloadSize(data, + attrs.filesize - data->state.resume_from); + SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); + } + + /* Setup the actual download */ + if(data->req.size == 0) { + /* no data to transfer */ + Curl_xfer_setup_nop(data); + infof(data, "File already completely downloaded"); + state(data, SSH_STOP); + return CURLE_OK; + } + Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); + + /* not set by Curl_xfer_setup to preserve keepon bits */ + data->conn->writesockfd = data->conn->sockfd; + + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh2 recv function will deal + with both accordingly */ + data->state.select_bits = CURL_CSELECT_IN; + state(data, SSH_STOP); + + return CURLE_OK; +} + +static CURLcode sftp_readdir(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *blockp) +{ + CURLcode result = CURLE_OK; + int rc = libssh2_sftp_readdir_ex(sshc->sftp_handle, + sshp->readdir_filename, CURL_PATH_MAX, + sshp->readdir_longentry, CURL_PATH_MAX, + &sshp->readdir_attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) { + *blockp = TRUE; + return result; + } + if(rc > 0) { + size_t readdir_len = (size_t) rc; + sshp->readdir_filename[readdir_len] = '\0'; + + if(data->set.list_only) { + result = Curl_client_write(data, CLIENTWRITE_BODY, + sshp->readdir_filename, + readdir_len); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, + (char *)"\n", 1); + if(result) + return result; + } + else { + result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry); + + if(!result) { + if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && + ((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) == + LIBSSH2_SFTP_S_IFLNK)) { + Curl_dyn_init(&sshp->readdir_link, CURL_PATH_MAX); + result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path, + sshp->readdir_filename); + state(data, SSH_SFTP_READDIR_LINK); + } + else { + state(data, SSH_SFTP_READDIR_BOTTOM); + } + } + return result; + } + } + else if(rc == 0) { + state(data, SSH_SFTP_READDIR_DONE); + } + else if(rc < 0) { + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); + result = sftp_libssh2_error_to_CURLE(sftperr); + sshc->actualcode = result ? result : CURLE_SSH; + failf(data, "Could not open remote file for reading: %s :: %d", + sftp_libssh2_strerror(sftperr), + libssh2_session_last_errno(sshc->ssh_session)); + state(data, SSH_SFTP_CLOSE); + } + return result; +} /* - * ssh_statemach_act() runs the SSH state machine as far as it can without + * ssh_statemachine() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN * meaning it wants to be called again when the socket is ready */ -static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) +static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; struct SSHPROTO *sshp = data->req.p.ssh; struct ssh_conn *sshc = &conn->proto.sshc; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; + int rc = LIBSSH2_ERROR_NONE; - int ssherr; - unsigned long sftperr; - int seekerr = CURL_SEEKFUNC_OK; - size_t readdir_len; *block = 0; /* we are not blocking by default */ do { @@ -1002,7 +1721,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) FALLTHROUGH(); case SSH_S_STARTUP: - rc = session_startup(sshc->ssh_session, sock); + rc = session_startup(sshc->ssh_session, conn->sock[FIRSTSOCKET]); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } @@ -1053,12 +1772,12 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_AUTH_DONE); break; } - ssherr = libssh2_session_last_errno(sshc->ssh_session); - if(ssherr == LIBSSH2_ERROR_EAGAIN) + rc = libssh2_session_last_errno(sshc->ssh_session); + if(rc == LIBSSH2_ERROR_EAGAIN) rc = LIBSSH2_ERROR_EAGAIN; else { state(data, SSH_SESSION_FREE); - sshc->actualcode = libssh2_session_error_to_CURLE(ssherr); + sshc->actualcode = libssh2_session_error_to_CURLE(rc); } break; } @@ -1069,93 +1788,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_AUTH_PKEY_INIT: - /* - * Check the supported auth types in the order I feel is most secure - * with the requested type of authentication - */ - sshc->authed = FALSE; - - if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && - (strstr(sshc->authlist, "publickey") != NULL)) { - bool out_of_memory = FALSE; - - sshc->rsa_pub = sshc->rsa = NULL; - - if(data->set.str[STRING_SSH_PRIVATE_KEY]) - sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); - else { - /* To ponder about: should really the lib be messing about with the - HOME environment variable etc? */ - char *home = curl_getenv("HOME"); - struct_stat sbuf; - - /* If no private key file is specified, try some common paths. */ - if(home) { - /* Try ~/.ssh first. */ - sshc->rsa = aprintf("%s/.ssh/id_rsa", home); - if(!sshc->rsa) - out_of_memory = TRUE; - else if(stat(sshc->rsa, &sbuf)) { - Curl_safefree(sshc->rsa); - sshc->rsa = aprintf("%s/.ssh/id_dsa", home); - if(!sshc->rsa) - out_of_memory = TRUE; - else if(stat(sshc->rsa, &sbuf)) { - Curl_safefree(sshc->rsa); - } - } - free(home); - } - if(!out_of_memory && !sshc->rsa) { - /* Nothing found; try the current dir. */ - sshc->rsa = strdup("id_rsa"); - if(sshc->rsa && stat(sshc->rsa, &sbuf)) { - Curl_safefree(sshc->rsa); - sshc->rsa = strdup("id_dsa"); - if(sshc->rsa && stat(sshc->rsa, &sbuf)) { - Curl_safefree(sshc->rsa); - /* Out of guesses. Set to the empty string to avoid - * surprising info messages. */ - sshc->rsa = strdup(""); - } - } - } - } - - /* - * Unless the user explicitly specifies a public key file, let - * libssh2 extract the public key from the private key file. - * This is done by simply passing sshc->rsa_pub = NULL. - */ - if(data->set.str[STRING_SSH_PUBLIC_KEY] - /* treat empty string the same way as NULL */ - && data->set.str[STRING_SSH_PUBLIC_KEY][0]) { - sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); - if(!sshc->rsa_pub) - out_of_memory = TRUE; - } - - if(out_of_memory || !sshc->rsa) { - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->rsa_pub); - state(data, SSH_SESSION_FREE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - - sshc->passphrase = data->set.ssl.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); - - state(data, SSH_AUTH_PKEY); - } - else { - state(data, SSH_AUTH_PASS_INIT); - } + result = sftp_pkey_init(data, sshc); break; case SSH_AUTH_PKEY: @@ -1378,7 +2011,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ - conn->sockfd = sock; + conn->sockfd = conn->sock[FIRSTSOCKET]; conn->writesockfd = CURL_SOCKET_BAD; if(conn->handler->protocol == CURLPROTO_SFTP) { @@ -1413,21 +2046,18 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SFTP_REALPATH: - { - char tempHome[PATH_MAX]; - /* * Get the "home" directory */ rc = sftp_libssh2_realpath(sshc->sftp_session, ".", - tempHome, PATH_MAX-1); + sshp->readdir_filename, CURL_PATH_MAX); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } if(rc > 0) { /* It seems that this string is not always NULL terminated */ - tempHome[rc] = '\0'; - sshc->homedir = strdup(tempHome); + sshp->readdir_filename[rc] = '\0'; + sshc->homedir = strdup(sshp->readdir_filename); if(!sshc->homedir) { state(data, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_OUT_OF_MEMORY; @@ -1437,7 +2067,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) } else { /* Return the error type */ - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); if(sftperr) result = sftp_libssh2_error_to_CURLE(sftperr); else @@ -1450,7 +2080,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) state(data, SSH_STOP); break; } - } + /* This is the last step in the SFTP connect phase. Do note that while we get the homedir here, we get the "workingpath" in the DO action since the homedir will remain the same between request but the @@ -1460,217 +2090,44 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SFTP_QUOTE_INIT: - - result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); - if(result) { - sshc->actualcode = result; - state(data, SSH_STOP); - break; - } - - if(data->set.quote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.quote; - state(data, SSH_SFTP_QUOTE); - } - else { - state(data, SSH_SFTP_GETINFO); - } - break; - - case SSH_SFTP_POSTQUOTE_INIT: - if(data->set.postquote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.postquote; - state(data, SSH_SFTP_QUOTE); - } - else { - state(data, SSH_STOP); - } - break; - - case SSH_SFTP_QUOTE: - /* Send any quote commands */ - { - const char *cp; - - /* - * Support some of the "FTP" commands - * - * 'sshc->quote_item' is already verified to be non-NULL before it - * switched to this state. - */ - char *cmd = sshc->quote_item->data; - sshc->acceptfail = FALSE; - - /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server responds. */ - - if(cmd[0] == '*') { - cmd++; - sshc->acceptfail = TRUE; - } - - if(strcasecompare("pwd", cmd)) { - /* output debug output if that is requested */ - char *tmp = aprintf("257 \"%s\" is current directory.\n", - sshp->path); - if(!tmp) { - result = CURLE_OUT_OF_MEMORY; - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - break; - } - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4); - Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); - - /* this sends an FTP-like "header" to the header callback so that the - current directory can be read very similar to how it is read when - using ordinary FTP. */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); - if(result) { - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - } - else - state(data, SSH_SFTP_NEXT_QUOTE); - break; - } - - /* - * the arguments following the command must be separated from the - * command with a space so we can check for it unconditionally - */ - cp = strchr(cmd, ' '); - if(!cp) { - failf(data, "Syntax error command '%s', missing parameter", - cmd); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - - /* - * also, every command takes at least one argument so we get that - * first argument right now - */ - result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error: Bad first parameter to '%s'", cmd); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - - /* - * SFTP is a binary protocol, so we do not send text commands - * to the server. Instead, we scan for commands used by - * OpenSSH's sftp program and call the appropriate libssh2 - * functions. - */ - if(strncasecompare(cmd, "chgrp ", 6) || - strncasecompare(cmd, "chmod ", 6) || - strncasecompare(cmd, "chown ", 6) || - strncasecompare(cmd, "atime ", 6) || - strncasecompare(cmd, "mtime ", 6)) { - /* attribute change */ - - /* sshc->quote_path1 contains the mode to set */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in %s: Bad second parameter", cmd); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); - state(data, SSH_SFTP_QUOTE_STAT); - break; - } - if(strncasecompare(cmd, "ln ", 3) || - strncasecompare(cmd, "symlink ", 8)) { - /* symbolic linking */ - /* sshc->quote_path1 is the source */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, - "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - state(data, SSH_SFTP_QUOTE_SYMLINK); - break; - } - else if(strncasecompare(cmd, "mkdir ", 6)) { - /* create dir */ - state(data, SSH_SFTP_QUOTE_MKDIR); - break; - } - else if(strncasecompare(cmd, "rename ", 7)) { - /* rename file */ - /* first param is the source path */ - /* second param is the dest. path */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - break; - } - state(data, SSH_SFTP_QUOTE_RENAME); + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + if(result) { + sshc->actualcode = result; + state(data, SSH_STOP); break; } - else if(strncasecompare(cmd, "rmdir ", 6)) { - /* delete dir */ - state(data, SSH_SFTP_QUOTE_RMDIR); - break; + + if(data->set.quote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.quote; + state(data, SSH_SFTP_QUOTE); } - else if(strncasecompare(cmd, "rm ", 3)) { - state(data, SSH_SFTP_QUOTE_UNLINK); - break; + else { + state(data, SSH_SFTP_GETINFO); } -#ifdef HAS_STATVFS_SUPPORT - else if(strncasecompare(cmd, "statvfs ", 8)) { - state(data, SSH_SFTP_QUOTE_STATVFS); - break; + break; + + case SSH_SFTP_POSTQUOTE_INIT: + if(data->set.postquote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.postquote; + state(data, SSH_SFTP_QUOTE); } -#endif + else { + state(data, SSH_STOP); + } + break; - failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; + case SSH_SFTP_QUOTE: + /* Send quote commands */ + result = sftp_quote(data, sshc, sshp); + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } break; - } case SSH_SFTP_NEXT_QUOTE: Curl_safefree(sshc->quote_path1); @@ -1693,125 +2150,13 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SFTP_QUOTE_STAT: - { - char *cmd = sshc->quote_item->data; - sshc->acceptfail = FALSE; - - /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server responds. */ - - if(cmd[0] == '*') { - cmd++; - sshc->acceptfail = TRUE; - } - - if(!strncasecompare(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. - */ - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, - curlx_uztoui(strlen(sshc->quote_path2)), - LIBSSH2_SFTP_STAT, - &sshp->quote_attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc && !sshc->acceptfail) { /* get those attributes */ - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to get SFTP stats failed: %s", - sftp_libssh2_strerror(sftperr)); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - - /* Now set the new attributes... */ - if(strncasecompare(cmd, "chgrp", 5)) { - sshp->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10); - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chgrp gid not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - else if(strncasecompare(cmd, "chmod", 5)) { - sshp->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8); - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; - /* permissions are octal */ - if(sshp->quote_attrs.permissions == 0 && - !ISDIGIT(sshc->quote_path1[0])) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chmod permissions not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - else if(strncasecompare(cmd, "chown", 5)) { - sshp->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10); - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chown uid not a number"); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - } - else if(strncasecompare(cmd, "atime", 5) || - strncasecompare(cmd, "mtime", 5)) { - time_t date = Curl_getdate_capped(sshc->quote_path1); - bool fail = FALSE; - - if(date == -1) { - failf(data, "incorrect date format for %.*s", 5, cmd); - fail = TRUE; - } -#if SIZEOF_TIME_T > SIZEOF_LONG - if(date > 0xffffffff) { - /* if 'long' cannot old >32-bit, this date cannot be sent */ - failf(data, "date overflow"); - fail = TRUE; - } -#endif - if(fail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - state(data, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - if(strncasecompare(cmd, "atime", 5)) - sshp->quote_attrs.atime = (unsigned long)date; - else /* mtime */ - sshp->quote_attrs.mtime = (unsigned long)date; - - sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_ACMODTIME; + result = sftp_quote_stat(data, sshc, sshp, block); + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; } - - /* Now send the completed structure... */ - state(data, SSH_SFTP_QUOTE_SETSTAT); break; - } case SSH_SFTP_QUOTE_SETSTAT: rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, @@ -1822,7 +2167,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); failf(data, "Attempt to set SFTP stats failed: %s", @@ -1845,7 +2190,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); failf(data, "symlink command failed: %s", @@ -1866,7 +2211,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(sftperr)); @@ -1891,7 +2236,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); Curl_safefree(sshc->quote_path2); failf(data, "rename command failed: %s", @@ -1911,7 +2256,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(sftperr)); @@ -1930,7 +2275,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr)); state(data, SSH_SFTP_CLOSE); @@ -1953,7 +2298,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } if(rc && !sshc->acceptfail) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); failf(data, "statvfs command failed: %s", sftp_libssh2_strerror(sftperr)); @@ -1963,11 +2308,11 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } else if(rc == 0) { - #ifdef _MSC_VER - #define CURL_LIBSSH2_VFS_SIZE_MASK "I64u" - #else - #define CURL_LIBSSH2_VFS_SIZE_MASK "llu" - #endif +#ifdef _MSC_VER +#define CURL_LIBSSH2_VFS_SIZE_MASK "I64u" +#else +#define CURL_LIBSSH2_VFS_SIZE_MASK "llu" +#endif char *tmp = aprintf("statvfs:\n" "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n" @@ -2046,188 +2391,13 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SFTP_UPLOAD_INIT: - { - unsigned long flags; - /* - * NOTE!!! libssh2 requires that the destination path is a full path - * that includes the destination file and name OR ends in a "/" - * If this is not done the destination file will be named the - * same name as the last directory in the path. - */ - - if(data->state.resume_from) { - LIBSSH2_SFTP_ATTRIBUTES attrs; - if(data->state.resume_from < 0) { - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_SFTP_STAT, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc) { - data->state.resume_from = 0; - } - else { - curl_off_t size = attrs.filesize; - if(size < 0) { - failf(data, "Bad file size (%" FMT_OFF_T ")", size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - data->state.resume_from = attrs.filesize; - } - } - } - - if(data->set.remote_append) - /* Try to open for append, but create if nonexisting */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND; - else if(data->state.resume_from > 0) - /* If we have restart position then open for append */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND; - else - /* Clear file before writing (normal behavior) */ - flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_TRUNC; - - sshc->sftp_handle = - libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - flags, (long)data->set.new_file_perms, - LIBSSH2_SFTP_OPENFILE); - - if(!sshc->sftp_handle) { - rc = libssh2_session_last_errno(sshc->ssh_session); - - if(LIBSSH2_ERROR_EAGAIN == rc) - break; - - if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc) - /* only when there was an SFTP protocol error can we extract - the sftp error! */ - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - else - sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */ - - if(sshc->secondCreateDirs) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = sftperr != LIBSSH2_FX_OK ? - sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH; - failf(data, "Creating the dir/file failed: %s", - sftp_libssh2_strerror(sftperr)); - break; - } - if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) || - (sftperr == LIBSSH2_FX_FAILURE) || - (sftperr == LIBSSH2_FX_NO_SUCH_PATH)) && - (data->set.ftp_create_missing_dirs && - (strlen(sshp->path) > 1))) { - /* try to create the path remotely */ - rc = 0; /* clear rc and continue */ - sshc->secondCreateDirs = 1; - state(data, SSH_SFTP_CREATE_DIRS_INIT); - break; - } - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = sftperr != LIBSSH2_FX_OK ? - sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH; - if(!sshc->actualcode) { - /* Sometimes, for some reason libssh2_sftp_last_error() returns zero - even though libssh2_sftp_open() failed previously! We need to - work around that! */ - sshc->actualcode = CURLE_SSH; - sftperr = LIBSSH2_FX_OK; - } - failf(data, "Upload failed: %s (%lu/%d)", - sftperr != LIBSSH2_FX_OK ? - sftp_libssh2_strerror(sftperr) : "ssh error", - sftperr, rc); - break; - } - - /* If we have a restart point then we need to seek to the correct - position. */ - if(data->state.resume_from > 0) { - /* Let's read off the proper amount of bytes from the input. */ - if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); - seekerr = data->set.seek_func(data->set.seek_client, - data->state.resume_from, SEEK_SET); - Curl_set_in_callback(data, FALSE); - } - - if(seekerr != CURL_SEEKFUNC_OK) { - curl_off_t passed = 0; - - if(seekerr != CURL_SEEKFUNC_CANTSEEK) { - failf(data, "Could not seek stream"); - return CURLE_FTP_COULDNT_USE_REST; - } - /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ - do { - char scratch[4*1024]; - size_t readthisamountnow = - (data->state.resume_from - passed > - (curl_off_t)sizeof(scratch)) ? - sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed); - - size_t actuallyread; - Curl_set_in_callback(data, TRUE); - actuallyread = data->state.fread_func(scratch, 1, - readthisamountnow, - data->state.in); - Curl_set_in_callback(data, FALSE); - - passed += actuallyread; - if((actuallyread == 0) || (actuallyread > readthisamountnow)) { - /* this checks for greater-than only to make sure that the - CURL_READFUNC_ABORT return code still aborts */ - failf(data, "Failed to read data"); - return CURLE_FTP_COULDNT_USE_REST; - } - } while(passed < data->state.resume_from); - } - - /* now, decrease the size of the read */ - if(data->state.infilesize > 0) { - data->state.infilesize -= data->state.resume_from; - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - - SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); - } - if(data->state.infilesize > 0) { - data->req.size = data->state.infilesize; - Curl_pgrsSetUploadSize(data, data->state.infilesize); - } - /* upload data */ - Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - conn->sockfd = conn->writesockfd; - + result = sftp_upload_init(data, sshc, sshp, block); if(result) { state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; } - else { - /* store this original bitmask setup to use later on if we cannot - figure out a "real" bitmask */ - sshc->orig_waitfor = data->req.keepon; - - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh2 sftp send function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_OUT; - - /* since we do not really wait for anything at this point, we want the - state machine to move on as soon as possible so we set a very short - timeout here */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - - state(data, SSH_STOP); - } break; - } case SSH_SFTP_CREATE_DIRS_INIT: if(strlen(sshp->path) > 1) { @@ -2267,7 +2437,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) * permission was denied (creation might succeed further down the * path) - retry on unspecific FAILURE also */ - sftperr = libssh2_sftp_last_error(sshc->sftp_session); + unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) && (sftperr != LIBSSH2_FX_FAILURE) && (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) { @@ -2292,12 +2462,12 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) * This is a directory that we are trying to get, so produce a directory * listing */ - sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, - sshp->path, - curlx_uztoui( - strlen(sshp->path)), - 0, 0, LIBSSH2_SFTP_OPENDIR); + sshc->sftp_handle = + libssh2_sftp_open_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + 0, 0, LIBSSH2_SFTP_OPENDIR); if(!sshc->sftp_handle) { + unsigned long sftperr; if(libssh2_session_last_errno(sshc->ssh_session) == LIBSSH2_ERROR_EAGAIN) { rc = LIBSSH2_ERROR_EAGAIN; @@ -2311,91 +2481,15 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) sshc->actualcode = result ? result : CURLE_SSH; break; } - sshp->readdir_filename = malloc(PATH_MAX + 1); - if(!sshp->readdir_filename) { - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - sshp->readdir_longentry = malloc(PATH_MAX + 1); - if(!sshp->readdir_longentry) { - Curl_safefree(sshp->readdir_filename); - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - Curl_dyn_init(&sshp->readdir, PATH_MAX * 2); + Curl_dyn_init(&sshp->readdir, CURL_PATH_MAX * 2); state(data, SSH_SFTP_READDIR); break; case SSH_SFTP_READDIR: - rc = libssh2_sftp_readdir_ex(sshc->sftp_handle, - sshp->readdir_filename, - PATH_MAX, - sshp->readdir_longentry, - PATH_MAX, - &sshp->readdir_attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc > 0) { - readdir_len = (size_t) rc; - sshp->readdir_filename[readdir_len] = '\0'; - - if(data->set.list_only) { - result = Curl_client_write(data, CLIENTWRITE_BODY, - sshp->readdir_filename, - readdir_len); - if(!result) - result = Curl_client_write(data, CLIENTWRITE_BODY, - (char *)"\n", 1); - if(result) { - state(data, SSH_STOP); - break; - } - - } - else { - result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry); - - if(!result) { - if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) && - ((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) == - LIBSSH2_SFTP_S_IFLNK)) { - Curl_dyn_init(&sshp->readdir_link, PATH_MAX); - result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path, - sshp->readdir_filename); - state(data, SSH_SFTP_READDIR_LINK); - if(!result) - break; - } - else { - state(data, SSH_SFTP_READDIR_BOTTOM); - break; - } - } - sshc->actualcode = result; - state(data, SSH_SFTP_CLOSE); - break; - } - } - else if(rc == 0) { - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); - state(data, SSH_SFTP_READDIR_DONE); - break; - } - else if(rc < 0) { - sftperr = libssh2_sftp_last_error(sshc->sftp_session); - result = sftp_libssh2_error_to_CURLE(sftperr); - sshc->actualcode = result ? result : CURLE_SSH; - failf(data, "Could not open remote file for reading: %s :: %d", - sftp_libssh2_strerror(sftperr), - libssh2_session_last_errno(sshc->ssh_session)); - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); + result = sftp_readdir(data, sshc, sshp, block); + if(result) { + sshc->actualcode = result; state(data, SSH_SFTP_CLOSE); - break; } break; @@ -2406,7 +2500,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) (unsigned int) Curl_dyn_len(&sshp->readdir_link), sshp->readdir_filename, - PATH_MAX, LIBSSH2_SFTP_READLINK); + CURL_PATH_MAX, LIBSSH2_SFTP_READLINK); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } @@ -2416,8 +2510,6 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename); if(result) { - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); state(data, SSH_SFTP_CLOSE); sshc->actualcode = result; break; @@ -2450,8 +2542,6 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; } sshc->sftp_handle = NULL; - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); /* no data to transfer */ Curl_xfer_setup_nop(data); @@ -2468,6 +2558,7 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) LIBSSH2_FXF_READ, (long)data->set.new_file_perms, LIBSSH2_SFTP_OPENFILE); if(!sshc->sftp_handle) { + unsigned long sftperr; if(libssh2_session_last_errno(sshc->ssh_session) == LIBSSH2_ERROR_EAGAIN) { rc = LIBSSH2_ERROR_EAGAIN; @@ -2485,139 +2576,13 @@ static CURLcode ssh_statemach_act(struct Curl_easy *data, bool *block) break; case SSH_SFTP_DOWNLOAD_STAT: - { - LIBSSH2_SFTP_ATTRIBUTES attrs; - - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_SFTP_STAT, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - break; - } - if(rc || - !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) || - (attrs.filesize == 0)) { - /* - * libssh2_sftp_open() did not return an error, so maybe the server - * just does not support stat() - * OR the server does not return a file size with a stat() - * OR file size is 0 - */ - data->req.size = -1; - data->req.maxdownload = -1; - Curl_pgrsSetDownloadSize(data, -1); - } - else { - curl_off_t size = attrs.filesize; - - if(size < 0) { - failf(data, "Bad file size (%" FMT_OFF_T ")", size); - return CURLE_BAD_DOWNLOAD_RESUME; - } - if(data->state.use_range) { - curl_off_t from, to; - char *ptr; - char *ptr2; - CURLofft to_t; - CURLofft from_t; - - from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); - if(from_t == CURL_OFFT_FLOW) - return CURLE_RANGE_ERROR; - while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) - ptr++; - to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); - if(to_t == CURL_OFFT_FLOW) - return CURLE_RANGE_ERROR; - if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ - || (to >= size)) { - to = size - 1; - } - if(from_t) { - /* from is relative to end of file */ - from = size - to; - to = size - 1; - } - if(from > size) { - failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" - FMT_OFF_T ")", from, (curl_off_t)attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - if(from > to) { - from = to; - size = 0; - } - else { - if((to - from) == CURL_OFF_T_MAX) - return CURLE_RANGE_ERROR; - size = to - from + 1; - } - - SFTP_SEEK(sshc->sftp_handle, from); - } - data->req.size = size; - data->req.maxdownload = size; - Curl_pgrsSetDownloadSize(data, size); - } - - /* We can resume if we can seek to the resume position */ - if(data->state.resume_from) { - if(data->state.resume_from < 0) { - /* We are supposed to download the last abs(from) bytes */ - if((curl_off_t)attrs.filesize < -data->state.resume_from) { - failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" - FMT_OFF_T ")", - data->state.resume_from, (curl_off_t)attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - /* download from where? */ - data->state.resume_from += attrs.filesize; - } - else { - if((curl_off_t)attrs.filesize < data->state.resume_from) { - failf(data, "Offset (%" FMT_OFF_T - ") was beyond file size (%" FMT_OFF_T ")", - data->state.resume_from, (curl_off_t)attrs.filesize); - return CURLE_BAD_DOWNLOAD_RESUME; - } - } - /* Now store the number of bytes we are expected to download */ - data->req.size = attrs.filesize - data->state.resume_from; - data->req.maxdownload = attrs.filesize - data->state.resume_from; - Curl_pgrsSetDownloadSize(data, - attrs.filesize - data->state.resume_from); - SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); + result = sftp_download_stat(data, sshc, sshp, block); + if(result) { + state(data, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; } - } - - /* Setup the actual download */ - if(data->req.size == 0) { - /* no data to transfer */ - Curl_xfer_setup_nop(data); - infof(data, "File already completely downloaded"); - state(data, SSH_STOP); break; - } - Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - conn->writesockfd = conn->sockfd; - - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh2 recv function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_IN; - - if(result) { - /* this should never occur; the close state should be entered - at the time the error occurs */ - state(data, SSH_SFTP_CLOSE); - sshc->actualcode = result; - } - else { - state(data, SSH_STOP); - } - break; case SSH_SFTP_CLOSE: if(sshc->sftp_handle) { @@ -3101,8 +3066,8 @@ static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done) bool block; /* we store the status and use that to provide a ssh_getsock() implementation */ do { - result = ssh_statemach_act(data, &block); - *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; + result = ssh_statemachine(data, &block); + *done = (sshc->state == SSH_STOP); /* if there is no error, it is not done and it did not EWOULDBLOCK, then try again */ } while(!result && !*done && !block); @@ -3124,7 +3089,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data, timediff_t left = 1000; struct curltime now = Curl_now(); - result = ssh_statemach_act(data, &block); + result = ssh_statemachine(data, &block); if(result) break; @@ -3513,8 +3478,6 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status) result = status; Curl_safefree(sshp->path); - Curl_safefree(sshp->readdir_filename); - Curl_safefree(sshp->readdir_longentry); Curl_dyn_free(&sshp->readdir); if(Curl_pgrsDone(data)) @@ -3549,7 +3512,7 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex, /* libssh2_channel_write() returns int! */ nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len); - ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN) ? TRUE : FALSE); + ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN)); if(nwrite == LIBSSH2_ERROR_EAGAIN) { *err = CURLE_AGAIN; @@ -3574,7 +3537,7 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex, /* libssh2_channel_read() returns int */ nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len); - ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN) ? TRUE : FALSE); + ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN)); if(nread == LIBSSH2_ERROR_EAGAIN) { *err = CURLE_AGAIN; nread = -1; @@ -3687,7 +3650,7 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex, nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len); - ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN) ? TRUE : FALSE); + ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN)); if(nwrite == LIBSSH2_ERROR_EAGAIN) { *err = CURLE_AGAIN; @@ -3715,7 +3678,7 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, nread = libssh2_sftp_read(sshc->sftp_handle, mem, len); - ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN) ? TRUE : FALSE); + ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN)); if(nread == LIBSSH2_ERROR_EAGAIN) { *err = CURLE_AGAIN; diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index 2ed78649b9c182..f2d42a3de2aed8 100644 --- a/lib/vssh/ssh.h +++ b/lib/vssh/ssh.h @@ -39,6 +39,8 @@ #include #endif +#include "curl_path.h" + /**************************************************************************** * SSH unique setup ***************************************************************************/ @@ -109,6 +111,8 @@ typedef enum { SSH_LAST /* never used */ } sshstate; +#define CURL_PATH_MAX 1024 + /* this struct is used in the HandleData struct which is part of the Curl_easy, which means this is used on a per-easy handle basis. Everything that is strictly related to a connection is banned from this @@ -118,8 +122,8 @@ struct SSHPROTO { #ifdef USE_LIBSSH2 struct dynbuf readdir_link; struct dynbuf readdir; - char *readdir_filename; - char *readdir_longentry; + char readdir_filename[CURL_PATH_MAX + 1]; + char readdir_longentry[CURL_PATH_MAX + 1]; LIBSSH2_SFTP_ATTRIBUTES quote_attrs; /* used by the SFTP_QUOTE state */ diff --git a/lib/vssh/wolfssh.c b/lib/vssh/wolfssh.c index 2f1d556d4b4982..5400821129b3ce 100644 --- a/lib/vssh/wolfssh.c +++ b/lib/vssh/wolfssh.c @@ -908,7 +908,7 @@ static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done) implementation */ do { result = wssh_statemach_act(data, &block); - *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; + *done = (sshc->state == SSH_STOP); /* if there is no error, it is not done and it did not EWOULDBLOCK, then try again */ if(*done) { diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c index cf6ba5f07f281d..53fd4a6bc164ec 100644 --- a/lib/vtls/bearssl.c +++ b/lib/vtls/bearssl.c @@ -609,11 +609,15 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); if(ssl_config->primary.cache_session) { - void *session; + void *sdata; + size_t slen; + const br_ssl_session_parameters *session; CURL_TRC_CF(data, cf, "connect_step1, check session cache"); Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &session, NULL)) { + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &sdata, &slen, NULL) && + slen == sizeof(*session)) { + session = sdata; br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); session_set = 1; infof(data, "BearSSL: reusing session ID"); @@ -761,7 +765,6 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, (struct bearssl_ssl_backend_data *)connssl->backend; br_ssl_session_parameters session; char cipher_str[64]; - char ver_str[16]; CURLcode ret; DEBUGASSERT(backend); @@ -772,6 +775,7 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, return CURLE_OK; if(ret == CURLE_OK) { unsigned int tver; + int subver = 0; if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { failf(data, "SSL: connection closed during handshake"); @@ -780,19 +784,22 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, connssl->connecting_state = ssl_connect_3; /* Informational message */ tver = br_ssl_engine_get_version(&backend->ctx.eng); - if(tver == BR_TLS12) - strcpy(ver_str, "TLSv1.2"); - else if(tver == BR_TLS11) - strcpy(ver_str, "TLSv1.1"); - else if(tver == BR_TLS10) - strcpy(ver_str, "TLSv1.0"); - else { - msnprintf(ver_str, sizeof(ver_str), "TLS 0x%04x", tver); + switch(tver) { + case BR_TLS12: + subver = 2; /* 1.2 */ + break; + case BR_TLS11: + subver = 1; /* 1.1 */ + break; + case BR_TLS10: /* 1.0 */ + default: /* unknown, leave it at zero */ + break; } br_ssl_engine_get_session_parameters(&backend->ctx.eng, &session); Curl_cipher_suite_get_str(session.cipher_suite, cipher_str, sizeof(cipher_str), TRUE); - infof(data, "BearSSL: %s connection using %s", ver_str, cipher_str); + infof(data, "BearSSL: TLS v1.%d connection using %s", subver, + cipher_str); } return ret; } @@ -820,7 +827,7 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, const char *proto; proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); - Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, + Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)proto, proto ? strlen(proto) : 0); } @@ -832,7 +839,8 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); Curl_ssl_sessionid_lock(data); - ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, session, 0, + ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + session, sizeof(*session), bearssl_session_free); Curl_ssl_sessionid_unlock(data); if(ret) diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index d3803501459d29..af4f0c349f2c31 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -50,6 +50,7 @@ #include "vauth/vauth.h" #include "parsedate.h" #include "connect.h" /* for the connect timeout */ +#include "progress.h" #include "select.h" #include "strcase.h" #include "warnless.h" @@ -284,7 +285,7 @@ static CURLcode handshake(struct Curl_cfilter *cf, } else if(0 == what) { if(nonblocking) - return CURLE_OK; + return CURLE_AGAIN; else if(timeout_ms) { /* timeout */ failf(data, "SSL connection timeout at %ld", (long)timeout_ms); @@ -719,45 +720,57 @@ static void gtls_sessionid_free(void *sessionid, size_t idsize) free(sessionid); } -static CURLcode gtls_update_session_id(struct Curl_cfilter *cf, - struct Curl_easy *data, - gnutls_session_t session) +CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session, + struct ssl_peer *peer, + const char *alpn) { struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_connect_data *connssl = cf->ctx; + void *connect_sessionid; + size_t connect_idsize = 0; CURLcode result = CURLE_OK; - if(ssl_config->primary.cache_session) { - /* we always unconditionally get the session id here, as even if we - already got it from the cache and asked to use it in the connection, it - might've been rejected and then a new one is in use now and we need to - detect that. */ - void *connect_sessionid; - size_t connect_idsize = 0; - - /* get the session ID data size */ - gnutls_session_get_data(session, NULL, &connect_idsize); - connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ - if(!connect_sessionid) { - return CURLE_OUT_OF_MEMORY; - } - else { - /* extract session ID to the allocated buffer */ - gnutls_session_get_data(session, connect_sessionid, &connect_idsize); - - CURL_TRC_CF(data, cf, "get session id (len=%zu) and store in cache", - connect_idsize); - Curl_ssl_sessionid_lock(data); - /* store this session id, takes ownership */ - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, - connect_sessionid, connect_idsize, - gtls_sessionid_free); - Curl_ssl_sessionid_unlock(data); - } - } + if(!ssl_config->primary.cache_session) + return CURLE_OK; + + /* we always unconditionally get the session id here, as even if we + already got it from the cache and asked to use it in the connection, it + might've been rejected and then a new one is in use now and we need to + detect that. */ + + /* get the session ID data size */ + gnutls_session_get_data(session, NULL, &connect_idsize); + if(!connect_idsize) /* gnutls does this for some version combinations */ + return CURLE_OK; + + connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ + if(!connect_sessionid) + return CURLE_OUT_OF_MEMORY; + + /* extract session ID to the allocated buffer */ + gnutls_session_get_data(session, connect_sessionid, &connect_idsize); + + CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s) and store in cache", + connect_idsize, alpn ? alpn : "-"); + Curl_ssl_sessionid_lock(data); + /* store this session id, takes ownership */ + result = Curl_ssl_set_sessionid(cf, data, peer, alpn, + connect_sessionid, connect_idsize, + gtls_sessionid_free); + Curl_ssl_sessionid_unlock(data); return result; } +static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session) +{ + struct ssl_connect_data *connssl = cf->ctx; + return Curl_gtls_update_session_id(cf, data, session, &connssl->peer, + connssl->alpn_negotiated); +} + static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, unsigned when, unsigned int incoming, const gnutls_datum_t *msg) @@ -773,7 +786,7 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, incoming ? "incoming" : "outgoing", htype); switch(htype) { case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { - gtls_update_session_id(cf, data, session); + cf_gtls_update_session_id(cf, data, session); break; } default: @@ -845,9 +858,13 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, init_flags |= GNUTLS_FORCE_CLIENT_CERT; #endif -#if defined(GNUTLS_NO_TICKETS) - /* Disable TLS session tickets */ - init_flags |= GNUTLS_NO_TICKETS; +#if defined(GNUTLS_NO_TICKETS_TLS12) + init_flags |= GNUTLS_NO_TICKETS_TLS12; +#elif defined(GNUTLS_NO_TICKETS) + /* Disable TLS session tickets for non 1.3 connections */ + if((config->version != CURL_SSLVERSION_TLSv1_3) && + (config->version != CURL_SSLVERSION_DEFAULT)) + init_flags |= GNUTLS_NO_TICKETS; #endif #if defined(GNUTLS_NO_STATUS_REQUEST) @@ -1034,11 +1051,15 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, struct Curl_easy *data, struct ssl_peer *peer, const unsigned char *alpn, size_t alpn_len, + struct ssl_connect_data *connssl, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + gnutls_datum_t gtls_alpns[5]; + size_t gtls_alpns_count = 0; CURLcode result; DEBUGASSERT(gctx); @@ -1061,52 +1082,91 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, gnutls_session_set_keylog_function(gctx->session, keylog_callback); } - /* convert the ALPN string from our arguments to a list of strings - * that gnutls wants and will convert internally back to this very - * string for sending to the server. nice. */ - if(alpn && alpn_len) { - gnutls_datum_t alpns[5]; - size_t i, alen = alpn_len; - unsigned char *s = (unsigned char *)alpn; - unsigned char slen; - for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) { - slen = s[0]; - if(slen >= alen) - return CURLE_FAILED_INIT; - alpns[i].data = s + 1; - alpns[i].size = slen; - s += slen + 1; - alen -= (size_t)slen + 1; - } - if(alen) /* not all alpn chars used, wrong format or too many */ - return CURLE_FAILED_INIT; - if(i && gnutls_alpn_set_protocols(gctx->session, - alpns, (unsigned int)i, - GNUTLS_ALPN_MANDATORY)) { - failf(data, "failed setting ALPN"); - return CURLE_SSL_CONNECT_ERROR; - } - } - /* This might be a reconnect, so we check for a session ID in the cache to speed up things */ if(conn_config->cache_session) { void *ssl_sessionid; size_t ssl_idsize; - + char *session_alpn; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, &ssl_idsize)) { + if(!Curl_ssl_getsessionid(cf, data, peer, + &ssl_sessionid, &ssl_idsize, &session_alpn)) { /* we got a session id, use it! */ int rc; rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize); if(rc < 0) infof(data, "SSL failed to set session ID"); - else - infof(data, "SSL reusing session ID (size=%zu)", ssl_idsize); + else { + infof(data, "SSL reusing session ID (size=%zu, alpn=%s)", + ssl_idsize, session_alpn ? session_alpn : "-"); +#ifdef DEBUGBUILD + if((ssl_config->earlydata || !!getenv("CURL_USE_EARLYDATA")) && +#else + if(ssl_config->earlydata && +#endif + !cf->conn->connect_only && connssl && + (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3) && + Curl_alpn_contains_proto(connssl->alpn, session_alpn)) { + connssl->earlydata_max = + gnutls_record_get_max_early_data_size(gctx->session); + if((!connssl->earlydata_max || + connssl->earlydata_max == 0xFFFFFFFFUL)) { + /* Seems to be GnuTLS way to signal no EarlyData in session */ + CURL_TRC_CF(data, cf, "TLS session does not allow earlydata"); + } + else { + CURL_TRC_CF(data, cf, "TLS session allows %zu earlydata bytes, " + "reusing ALPN '%s'", + connssl->earlydata_max, session_alpn); + connssl->earlydata_state = ssl_earlydata_use; + connssl->state = ssl_connection_deferred; + result = Curl_alpn_set_negotiated(cf, data, connssl, + (const unsigned char *)session_alpn, + session_alpn ? strlen(session_alpn) : 0); + if(result) + return result; + /* We only try the ALPN protocol the session used before, + * otherwise we might send early data for the wrong protocol */ + gtls_alpns[0].data = (unsigned char *)session_alpn; + gtls_alpns[0].size = (unsigned)strlen(session_alpn); + gtls_alpns_count = 1; + } + } + } } Curl_ssl_sessionid_unlock(data); } + + /* convert the ALPN string from our arguments to a list of strings that + * gnutls wants and will convert internally back to this string for sending + * to the server. nice. */ + if(!gtls_alpns_count && alpn && alpn_len) { + size_t i, alen = alpn_len; + unsigned char *s = (unsigned char *)alpn; + unsigned char slen; + for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) { + slen = s[0]; + if(slen >= alen) + return CURLE_FAILED_INIT; + gtls_alpns[i].data = s + 1; + gtls_alpns[i].size = slen; + s += slen + 1; + alen -= (size_t)slen + 1; + } + if(alen) /* not all alpn chars used, wrong format or too many */ + return CURLE_FAILED_INIT; + gtls_alpns_count = i; + } + + if(gtls_alpns_count && + gnutls_alpn_set_protocols(gctx->session, + gtls_alpns, (unsigned int)gtls_alpns_count, + GNUTLS_ALPN_MANDATORY)) { + failf(data, "failed setting ALPN"); + return CURLE_SSL_CONNECT_ERROR; + } + return CURLE_OK; } @@ -1137,10 +1197,15 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) } result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer, - proto.data, proto.len, NULL, NULL, cf); + proto.data, proto.len, connssl, NULL, NULL, cf); if(result) return result; + if(connssl->alpn && (connssl->state != ssl_connection_deferred)) { + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); + } + gnutls_handshake_set_hook_function(backend->gtls.session, GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, gtls_handshake_cb); @@ -1675,26 +1740,79 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf, if(result) goto out; - if(connssl->alpn) { - gnutls_datum_t proto; - int rc; - - rc = gnutls_alpn_get_selected_protocol(session, &proto); - if(rc == 0) - Curl_alpn_set_negotiated(cf, data, proto.data, proto.size); - else - Curl_alpn_set_negotiated(cf, data, NULL, 0); - } - /* Only on TLSv1.2 or lower do we have the session id now. For * TLSv1.3 we get it via a SESSION_TICKET message that arrives later. */ if(gnutls_protocol_get_version(session) < GNUTLS_TLS1_3) - result = gtls_update_session_id(cf, data, session); + result = cf_gtls_update_session_id(cf, data, session); out: return result; } +static CURLcode gtls_set_earlydata(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t blen) +{ + struct ssl_connect_data *connssl = cf->ctx; + ssize_t nwritten = 0; + CURLcode result = CURLE_OK; + + DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use); + DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata)); + if(blen) { + if(blen > connssl->earlydata_max) + blen = connssl->earlydata_max; + nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result); + CURL_TRC_CF(data, cf, "gtls_set_earlydata(len=%zu) -> %zd", + blen, nwritten); + if(nwritten < 0) + return result; + } + connssl->earlydata_state = ssl_earlydata_sending; + connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata); + return CURLE_OK; +} + +static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct ssl_connect_data *connssl = cf->ctx; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; + const unsigned char *buf; + size_t blen; + ssize_t n; + + DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending); + backend->gtls.io_result = CURLE_OK; + while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) { + n = gnutls_record_send_early_data(backend->gtls.session, buf, blen); + CURL_TRC_CF(data, cf, "gtls_send_earlydata(len=%zu) -> %zd", + blen, n); + if(n < 0) { + if(n == GNUTLS_E_AGAIN) + result = CURLE_AGAIN; + else + result = backend->gtls.io_result ? + backend->gtls.io_result : CURLE_SEND_ERROR; + goto out; + } + else if(!n) { + /* gnutls is buggy, it *SHOULD* return the amount of bytes it took in. + * Instead it returns 0 if everything was written. */ + n = (ssize_t)blen; + } + + Curl_bufq_skip(&connssl->earlydata, (size_t)n); + } + /* sent everything there was */ + infof(data, "SSL sending %" FMT_OFF_T " bytes of early data", + connssl->earlydata_skip); +out: + return result; +} + /* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. @@ -1708,46 +1826,89 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, bool nonblocking, - bool *done) -{ + bool *done) { struct ssl_connect_data *connssl = cf->ctx; - CURLcode rc; + struct gtls_ssl_backend_data *backend = + (struct gtls_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; + DEBUGASSERT(backend); + /* Initiate the connection, if not already done */ - if(ssl_connect_1 == connssl->connecting_state) { - rc = gtls_connect_step1(cf, data); - if(rc) { - result = rc; + if(connssl->connecting_state == ssl_connect_1) { + result = gtls_connect_step1(cf, data); + if(result) goto out; - } + connssl->connecting_state = ssl_connect_2; } - rc = handshake(cf, data, TRUE, nonblocking); - if(rc) { - /* handshake() sets its own error message with failf() */ - result = rc; - goto out; + if(connssl->connecting_state == ssl_connect_2) { + if(connssl->earlydata_state == ssl_earlydata_use) { + goto out; + } + else if(connssl->earlydata_state == ssl_earlydata_sending) { + result = gtls_send_earlydata(cf, data); + if(result) + goto out; + connssl->earlydata_state = ssl_earlydata_sent; + if(!Curl_ssl_cf_is_proxy(cf)) + Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip); + } + DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) || + (connssl->earlydata_state == ssl_earlydata_sent)); + + result = handshake(cf, data, TRUE, nonblocking); + if(result) + goto out; + connssl->connecting_state = ssl_connect_3; } /* Finish connecting once the handshake is done */ - if(ssl_connect_1 == connssl->connecting_state) { - struct gtls_ssl_backend_data *backend = - (struct gtls_ssl_backend_data *)connssl->backend; - gnutls_session_t session; - DEBUGASSERT(backend); - session = backend->gtls.session; - rc = gtls_verifyserver(cf, data, session); - if(rc) { - result = rc; + if(connssl->connecting_state == ssl_connect_3) { + gnutls_datum_t proto; + int rc; + result = gtls_verifyserver(cf, data, backend->gtls.session); + if(result) goto out; - } + connssl->state = ssl_connection_complete; + connssl->connecting_state = ssl_connect_1; + + rc = gnutls_alpn_get_selected_protocol(backend->gtls.session, &proto); + if(rc) { /* No ALPN from server */ + proto.data = NULL; + proto.size = 0; + } + + result = Curl_alpn_set_negotiated(cf, data, connssl, + proto.data, proto.size); + if(result) + goto out; + + if(connssl->earlydata_state == ssl_earlydata_sent) { + if(gnutls_session_get_flags(backend->gtls.session) & + GNUTLS_SFLAGS_EARLY_DATA) { + connssl->earlydata_state = ssl_earlydata_accepted; + infof(data, "Server accepted %zu bytes of TLS early data.", + connssl->earlydata_skip); + } + else { + connssl->earlydata_state = ssl_earlydata_rejected; + if(!Curl_ssl_cf_is_proxy(cf)) + Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip); + infof(data, "Server rejected TLS early data."); + connssl->earlydata_skip = 0; + } + } } out: - *done = ssl_connect_1 == connssl->connecting_state; - + if(result == CURLE_AGAIN) { + *done = FALSE; + return CURLE_OK; + } + *done = ((connssl->connecting_state == ssl_connect_1) || + (connssl->state == ssl_connection_deferred)); return result; } @@ -1755,6 +1916,12 @@ static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { + struct ssl_connect_data *connssl = cf->ctx; + if(connssl->state == ssl_connection_deferred) { + /* We refuse to be pushed, we are waiting for someone to send/recv. */ + *done = TRUE; + return CURLE_OK; + } return gtls_connect_common(cf, data, TRUE, done); } @@ -1773,6 +1940,26 @@ static CURLcode gtls_connect(struct Curl_cfilter *cf, return CURLE_OK; } +static CURLcode gtls_connect_deferred(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, + size_t blen, + bool *done) +{ + struct ssl_connect_data *connssl = cf->ctx; + CURLcode result = CURLE_OK; + + DEBUGASSERT(connssl->state == ssl_connection_deferred); + *done = FALSE; + if(connssl->earlydata_state == ssl_earlydata_use) { + result = gtls_set_earlydata(cf, data, buf, blen); + if(result) + return result; + } + + return gtls_connect_common(cf, data, TRUE, done); +} + static bool gtls_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { @@ -1800,8 +1987,38 @@ static ssize_t gtls_send(struct Curl_cfilter *cf, ssize_t rc; size_t nwritten, total_written = 0; - (void)data; DEBUGASSERT(backend); + + if(connssl->state == ssl_connection_deferred) { + bool done = FALSE; + *curlcode = gtls_connect_deferred(cf, data, buf, blen, &done); + if(*curlcode) { + rc = -1; + goto out; + } + else if(!done) { + *curlcode = CURLE_AGAIN; + rc = -1; + goto out; + } + DEBUGASSERT(connssl->state == ssl_connection_complete); + } + + if(connssl->earlydata_skip) { + if(connssl->earlydata_skip >= blen) { + connssl->earlydata_skip -= blen; + *curlcode = CURLE_OK; + rc = (ssize_t)blen; + goto out; + } + else { + total_written += connssl->earlydata_skip; + buf = ((const char *)buf) + connssl->earlydata_skip; + blen -= connssl->earlydata_skip; + connssl->earlydata_skip = 0; + } + } + while(blen) { backend->gtls.io_result = CURLE_OK; rc = gnutls_record_send(backend->gtls.session, buf, blen); @@ -1848,7 +2065,9 @@ static CURLcode gtls_shutdown(struct Curl_cfilter *cf, size_t i; DEBUGASSERT(backend); - if(!backend->gtls.session || cf->shutdown) { + /* If we have no handshaked connection or already shut down */ + if(!backend->gtls.session || cf->shutdown || + connssl->state != ssl_connection_complete) { *done = TRUE; goto out; } @@ -1946,7 +2165,21 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf, (void)data; DEBUGASSERT(backend); - backend->gtls.io_result = CURLE_OK; + if(connssl->state == ssl_connection_deferred) { + bool done = FALSE; + *curlcode = gtls_connect_deferred(cf, data, NULL, 0, &done); + if(*curlcode) { + ret = -1; + goto out; + } + else if(!done) { + *curlcode = CURLE_AGAIN; + ret = -1; + goto out; + } + DEBUGASSERT(connssl->state == ssl_connection_complete); + } + ret = gnutls_record_recv(backend->gtls.session, buf, buffersize); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { *curlcode = CURLE_AGAIN; diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index b0ca55bfb756bf..4f9c540ed27aaa 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -45,6 +45,7 @@ struct Curl_cfilter; struct ssl_primary_config; struct ssl_config_data; struct ssl_peer; +struct ssl_connect_data; struct gtls_shared_creds { gnutls_certificate_credentials_t creds; @@ -78,6 +79,7 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, struct Curl_easy *data, struct ssl_peer *peer, const unsigned char *alpn, size_t alpn_len, + struct ssl_connect_data *connssl, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data); @@ -93,6 +95,13 @@ CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, struct ssl_peer *peer, const char *pinned_key); +/* Extract TLS session and place in cache, if configured. */ +CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf, + struct Curl_easy *data, + gnutls_session_t session, + struct ssl_peer *peer, + const char *alpn); + extern const struct Curl_ssl Curl_ssl_gnutls; #endif /* USE_GNUTLS */ diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 0865bec470b8c1..e071ded72fc0bd 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -54,7 +54,7 @@ # ifdef MBEDTLS_DEBUG # include # endif -#endif +#endif /* MBEDTLS_VERSION_MAJOR >= 2 */ #include "cipher_suite.h" #include "strcase.h" @@ -118,7 +118,11 @@ struct mbed_ssl_backend_data { #define TLS13_SUPPORT #endif -#if defined(THREADING_SUPPORT) +#if defined(TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS) +#define HAS_SESSION_TICKETS +#endif + +#ifdef THREADING_SUPPORT static mbedtls_entropy_context ts_entropy; static int entropy_init_initialized = 0; @@ -291,7 +295,8 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, break; #endif default: - failf(data, "mbedTLS: unsupported minimum TLS version value"); + failf(data, "mbedTLS: unsupported minimum TLS version value: %x", + conn_config->version); return CURLE_SSL_CONNECT_ERROR; } @@ -340,6 +345,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, cipher suite present in other SSL implementations. Provide provisional support for specifying the cipher suite here. */ #ifdef MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8 +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 static int mbed_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, bool prefer_rfc) @@ -350,6 +356,7 @@ mbed_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, return Curl_cipher_suite_get_str(id, buf, buf_size, prefer_rfc); return 0; } +#endif static uint16_t mbed_cipher_suite_walk_str(const char **str, const char **end) @@ -578,16 +585,6 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_NOT_BUILT_IN; } -#ifdef TLS13_SUPPORT - ret = psa_crypto_init(); - if(ret != PSA_SUCCESS) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedTLS psa_crypto_init returned (-0x%04X) %s", - -ret, errorbuf); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* TLS13_SUPPORT */ - #ifdef THREADING_SUPPORT mbedtls_ctr_drbg_init(&backend->ctr_drbg); @@ -805,6 +802,12 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR; } +#ifdef MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED + /* New in mbedTLS 3.6.1, need to enable, default is now disabled */ + mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config, + MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED); +#endif + /* Always let mbedTLS verify certificates, if verifypeer or verifyhost are * disabled we clear the corresponding error flags in the verify callback * function. That is also where we log verification errors. */ @@ -872,17 +875,27 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Check if there is a cached ID we can/should use here! */ if(ssl_config->primary.cache_session) { - void *old_session = NULL; + void *sdata = NULL; + size_t slen = 0; Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &old_session, NULL)) { - ret = mbedtls_ssl_set_session(&backend->ssl, old_session); + if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, + &sdata, &slen, NULL) && slen) { + mbedtls_ssl_session session; + + mbedtls_ssl_session_init(&session); + ret = mbedtls_ssl_session_load(&session, sdata, slen); if(ret) { - Curl_ssl_sessionid_unlock(data); - failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; + failf(data, "error loading cached session: -0x%x", -ret); + } + else { + ret = mbedtls_ssl_set_session(&backend->ssl, &session); + if(ret) + failf(data, "error setting session: -0x%x", -ret); + else + infof(data, "SSL reusing session ID"); } - infof(data, "mbedTLS reusing session"); + mbedtls_ssl_session_free(&session); } Curl_ssl_sessionid_unlock(data); } @@ -1048,7 +1061,7 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der needs a non-const key, for now. - https://github.com/ARMmbed/mbedtls/issues/396 */ + https://github.com/Mbed-TLS/mbedtls/issues/396 */ #if MBEDTLS_VERSION_NUMBER == 0x03000000 if(mbedtls_x509_crt_parse_der(p, peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p), @@ -1091,7 +1104,7 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(connssl->alpn) { const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl); - Curl_alpn_set_negotiated(cf, data, (const unsigned char *)proto, + Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)proto, proto ? strlen(proto) : 0); } #endif @@ -1102,57 +1115,62 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OK; } -static void mbedtls_session_free(void *sessionid, size_t idsize) +static void mbedtls_session_free(void *session, size_t slen) { - (void)idsize; - mbedtls_ssl_session_free(sessionid); - free(sessionid); + (void)slen; + free(session); } static CURLcode -mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) +mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) { - CURLcode retcode = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + CURLcode result = CURLE_OK; - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); DEBUGASSERT(backend); - if(ssl_config->primary.cache_session) { int ret; - mbedtls_ssl_session *our_ssl_sessionid; + mbedtls_ssl_session session; + unsigned char *sdata = NULL; + size_t slen = 0; - our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); - if(!our_ssl_sessionid) - return CURLE_OUT_OF_MEMORY; - - mbedtls_ssl_session_init(our_ssl_sessionid); - - ret = mbedtls_ssl_get_session(&backend->ssl, our_ssl_sessionid); + mbedtls_ssl_session_init(&session); + ret = mbedtls_ssl_get_session(&backend->ssl, &session); if(ret) { if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED) - mbedtls_ssl_session_free(our_ssl_sessionid); - free(our_ssl_sessionid); + mbedtls_ssl_session_free(&session); failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); return CURLE_SSL_CONNECT_ERROR; } - /* If there is already a matching session in the cache, delete it */ - Curl_ssl_sessionid_lock(data); - retcode = Curl_ssl_set_sessionid(cf, data, &connssl->peer, - our_ssl_sessionid, 0, - mbedtls_session_free); - Curl_ssl_sessionid_unlock(data); - if(retcode) - return retcode; + mbedtls_ssl_session_save(&session, NULL, 0, &slen); + if(!slen) { + failf(data, "failed to serialize session: length is 0"); + } + else { + sdata = malloc(slen); + if(sdata) { + ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen); + if(ret) { + failf(data, "failed to serialize session: -0x%x", -ret); + } + else { + Curl_ssl_sessionid_lock(data); + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + sdata, slen, mbedtls_session_free); + Curl_ssl_sessionid_unlock(data); + if(!result) + sdata = NULL; + } + } + } + mbedtls_ssl_session_free(&session); + free(sdata); } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; + return result; } static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1311,7 +1329,6 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; int ret = -1; - ssize_t len = -1; (void)data; DEBUGASSERT(backend); @@ -1321,24 +1338,31 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(ret <= 0) { CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", buffersize, -ret); - if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) - return 0; - *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_READ) -#ifdef TLS13_SUPPORT - || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + switch(ret) { +#ifdef HAS_SESSION_TICKETS + case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: + mbed_new_session(cf, data); + FALLTHROUGH(); #endif - ) ? CURLE_AGAIN : CURLE_RECV_ERROR; - if(*curlcode != CURLE_AGAIN) { + case MBEDTLS_ERR_SSL_WANT_READ: + *curlcode = CURLE_AGAIN; + ret = -1; + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + *curlcode = CURLE_OK; + ret = 0; + break; + default: { char errorbuf[128]; mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "ssl_read returned: (-0x%04X) %s", -ret, errorbuf); + *curlcode = CURLE_RECV_ERROR; + ret = -1; + break; + } } - return -1; } - - len = ret; - - return len; + return (ssize_t)ret; } static size_t mbedtls_version(char *buffer, size_t size) @@ -1353,35 +1377,24 @@ static size_t mbedtls_version(char *buffer, size_t size) #endif } +/* 'data' might be NULL */ static CURLcode mbedtls_random(struct Curl_easy *data, unsigned char *entropy, size_t length) { #if defined(MBEDTLS_CTR_DRBG_C) - int ret = -1; - char errorbuf[128]; + int ret; mbedtls_entropy_context ctr_entropy; mbedtls_ctr_drbg_context ctr_drbg; mbedtls_entropy_init(&ctr_entropy); mbedtls_ctr_drbg_init(&ctr_drbg); + (void)data; ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &ctr_entropy, NULL, 0); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - } - else { + if(!ret) ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_random returned (-0x%04X) %s", - -ret, errorbuf); - } - } - mbedtls_ctr_drbg_free(&ctr_drbg); mbedtls_entropy_free(&ctr_entropy); @@ -1483,9 +1496,22 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data, } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - retcode = mbed_connect_step3(cf, data); - if(retcode) - return retcode; + /* For tls1.3 we get notified about new sessions */ +#if MBEDTLS_VERSION_NUMBER >= 0x03020000 + struct ssl_connect_data *ctx = cf->ctx; + struct mbed_ssl_backend_data *backend = + (struct mbed_ssl_backend_data *)ctx->backend; + + if(mbedtls_ssl_get_version_number(&backend->ssl) <= + MBEDTLS_SSL_VERSION_TLS1_2) { +#else + { /* no TLSv1.3 supported here */ +#endif + retcode = mbed_new_session(cf, data); + if(retcode) + return retcode; + } + connssl->connecting_state = ssl_connect_done; } if(ssl_connect_done == connssl->connecting_state) { @@ -1535,6 +1561,20 @@ static int mbedtls_init(void) #ifdef THREADING_SUPPORT entropy_init_mutex(&ts_entropy); #endif +#ifdef TLS13_SUPPORT + { + int ret; +#ifdef THREADING_SUPPORT + Curl_mbedtlsthreadlock_lock_function(0); +#endif + ret = psa_crypto_init(); +#ifdef THREADING_SUPPORT + Curl_mbedtlsthreadlock_unlock_function(0); +#endif + if(ret != PSA_SUCCESS) + return 0; + } +#endif /* TLS13_SUPPORT */ return 1; } diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 4d39d38dca4248..0643ba046efa36 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -83,7 +83,7 @@ #include #ifdef USE_ECH -# ifndef OPENSSL_IS_BORINGSSL +# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) # include # endif # include "curl_base64.h" @@ -247,7 +247,7 @@ #elif defined(OPENSSL_IS_AWSLC) #define OSSL_PACKAGE "AWS-LC" #else -# if defined(USE_NGTCP2) && defined(USE_NGHTTP3) +# if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_MSH3) # define OSSL_PACKAGE "quictls" # else # define OSSL_PACKAGE "OpenSSL" @@ -1015,7 +1015,7 @@ static int passwd_callback(char *buf, int num, int encrypting, */ static bool rand_enough(void) { - return (0 != RAND_status()) ? TRUE : FALSE; + return (0 != RAND_status()); } static CURLcode ossl_seed(struct Curl_easy *data) @@ -1935,8 +1935,9 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, /* SSL should now have started the shutdown from our side. Since it * was not complete, we are lacking the close notify from the server. */ - if(send_shutdown) { + if(send_shutdown && !(SSL_get_shutdown(octx->ssl) & SSL_SENT_SHUTDOWN)) { ERR_clear_error(); + CURL_TRC_CF(data, cf, "send SSL close notify"); if(SSL_shutdown(octx->ssl) == 1) { CURL_TRC_CF(data, cf, "SSL shutdown finished"); *done = TRUE; @@ -1961,7 +1962,10 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, err = SSL_get_error(octx->ssl, nread); switch(err) { case SSL_ERROR_ZERO_RETURN: /* no more data */ - CURL_TRC_CF(data, cf, "SSL shutdown not received, but closed"); + if(SSL_shutdown(octx->ssl) == 1) + CURL_TRC_CF(data, cf, "SSL shutdown finished"); + else + CURL_TRC_CF(data, cf, "SSL shutdown not received, but closed"); *done = TRUE; break; case SSL_ERROR_NONE: /* just did not get anything */ @@ -2679,9 +2683,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, "%s (%s), %s, %s (%d):\n", verstr, direction ? "OUT" : "IN", tls_rt_name, msg_name, msg_type); - if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) { - Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len); - } + Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len); } Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT : @@ -2914,7 +2916,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, } Curl_ssl_sessionid_lock(data); - result = Curl_ssl_set_sessionid(cf, data, peer, der_session_buf, + result = Curl_ssl_set_sessionid(cf, data, peer, NULL, der_session_buf, der_session_size, ossl_session_free); Curl_ssl_sessionid_unlock(data); } @@ -3672,14 +3674,14 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); #endif -#ifdef HAS_ALPN if(alpn && alpn_len) { +#ifdef HAS_ALPN if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) { failf(data, "Error setting ALPN"); return CURLE_SSL_CONNECT_ERROR; } - } #endif + } if(ssl_cert || ssl_cert_blob || ssl_cert_type) { if(!result && @@ -3847,15 +3849,15 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, if(data->set.tls_ech & CURLECH_GREASE) { infof(data, "ECH: will GREASE ClientHello"); -# ifdef OPENSSL_IS_BORINGSSL +# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) SSL_set_enable_ech_grease(octx->ssl, 1); # else SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE); # endif } else if(data->set.tls_ech & CURLECH_CLA_CFG) { -# ifdef OPENSSL_IS_BORINGSSL - /* have to do base64 decode here for boring */ +# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) + /* have to do base64 decode here for BoringSSL */ const char *b64 = data->set.str[STRING_ECH_CONFIG]; if(!b64) { @@ -3915,7 +3917,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, size_t elen = rinfo->echconfiglist_len; infof(data, "ECH: ECHConfig from DoH HTTPS RR"); -# ifndef OPENSSL_IS_BORINGSSL +# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) if(SSL_ech_set1_echconfig(octx->ssl, ecl, elen) != 1) { infof(data, "ECH: SSL_ECH_set1_echconfig failed"); if(data->set.tls_ech & CURLECH_HARD) @@ -3923,7 +3925,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } # else if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { - infof(data, "ECH: SSL_set1_ech_config_list failed (boring)"); + infof(data, "ECH: SSL_set1_ech_config_list failed (BoringSSL)"); if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } @@ -3941,7 +3943,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, Curl_resolv_unlink(data, &dns); } } -# ifdef OPENSSL_IS_BORINGSSL +# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) if(trying_ech_now && outername) { infof(data, "ECH: setting public_name not supported with BoringSSL"); return CURLE_SSL_CONNECT_ERROR; @@ -3958,7 +3960,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return CURLE_SSL_CONNECT_ERROR; } } -# endif /* not BORING */ +# endif /* OPENSSL_IS_BORINGSSL || OPENSSL_IS_AWSLC */ if(trying_ech_now && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) { infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); @@ -3970,10 +3972,10 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #endif octx->reused_session = FALSE; - if(ssl_config->primary.cache_session && transport == TRNSPRT_TCP) { + if(ssl_config->primary.cache_session) { Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid, - &der_sessionid_size)) { + &der_sessionid_size, NULL)) { /* we got a session id, use it! */ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid, (long)der_sessionid_size); @@ -4069,7 +4071,7 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl, CURLcode result = CURLE_OK; size_t rcl = 0; int rv = 1; -# ifndef OPENSSL_IS_BORINGSSL +# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) char *inner = NULL; unsigned char *rcs = NULL; char *outer = NULL; @@ -4084,7 +4086,7 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl, /* nothing to trace if not doing ECH */ if(!ECH_ENABLED(data)) return; -# ifndef OPENSSL_IS_BORINGSSL +# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) rv = SSL_ech_get_retry_config(ssl, &rcs, &rcl); # else SSL_get0_ech_retry_configs(ssl, &rcs, &rcl); @@ -4101,23 +4103,23 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl, if(!result && b64str) infof(data, "ECH: retry_configs %s", b64str); free(b64str); -# ifndef OPENSSL_IS_BORINGSSL +# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) rv = SSL_ech_get_status(ssl, &inner, &outer); infof(data, "ECH: retry_configs for %s from %s, %d %d", inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); -#else +# else rv = SSL_ech_accepted(ssl); servername_type = SSL_get_servername_type(ssl); inner = SSL_get_servername(ssl, servername_type); SSL_get0_ech_name_override(ssl, &outer, &out_name_len); - /* TODO: get the inner from boring */ + /* TODO: get the inner from BoringSSL */ infof(data, "ECH: retry_configs for %s from %s, %d %d", inner ? inner : "NULL", outer ? outer : "NULL", reason, rv); -#endif +# endif } else infof(data, "ECH: no retry_configs (rv = %d)", rv); -# ifndef OPENSSL_IS_BORINGSSL +# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) OPENSSL_free((void *)rcs); # endif return; @@ -4235,12 +4237,13 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, /* If client certificate is required, communicate the error to client */ result = CURLE_SSL_CLIENTCERT; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + failf(data, "TLS cert problem: %s", + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer))); } #endif #ifdef USE_ECH else if((lib == ERR_LIB_SSL) && -# ifndef OPENSSL_IS_BORINGSSL +# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) (reason == SSL_R_ECH_REQUIRED)) { # else (reason == SSL_R_ECH_REJECTED)) { @@ -4250,12 +4253,14 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, ossl_trace_ech_retry_configs(data, octx->ssl, reason); result = CURLE_ECH_REQUIRED; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + failf(data, "ECH required: %s", + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer))); } #endif else { result = CURLE_SSL_CONNECT_ERROR; - ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)); + failf(data, "TLS connect error: %s", + ossl_strerror(errdetail, error_buffer, sizeof(error_buffer))); } /* detail is already set to the SSL error above */ @@ -4276,9 +4281,6 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, return result; } - /* Could be a CERT problem */ - failf(data, "%s", error_buffer); - return result; } } @@ -4307,7 +4309,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, OBJ_nid2sn(psigtype_nid)); #ifdef USE_ECH -# ifndef OPENSSL_IS_BORINGSSL +# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) if(ECH_ENABLED(data)) { char *inner = NULL, *outer = NULL; const char *status = NULL; @@ -4365,7 +4367,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, else { infof(data, "ECH: result: status is not attempted"); } -# endif /* BORING */ +# endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ #endif /* USE_ECH */ #ifdef HAS_ALPN @@ -4377,7 +4379,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, unsigned int len; SSL_get0_alpn_selected(octx->ssl, &neg_protocol, &len); - return Curl_alpn_set_negotiated(cf, data, neg_protocol, len); + return Curl_alpn_set_negotiated(cf, data, connssl, neg_protocol, len); } #endif @@ -4710,7 +4712,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, bool incache; Curl_ssl_sessionid_lock(data); incache = !(Curl_ssl_getsessionid(cf, data, peer, - &old_ssl_sessionid, NULL)); + &old_ssl_sessionid, NULL, NULL)); if(incache) { infof(data, "Remove session ID again from cache"); Curl_ssl_delsessionid(data, old_ssl_sessionid); @@ -5130,9 +5132,8 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, } while(cf->next); if(!octx) { - failf(data, - "Failed to find SSL backend for endpoint"); - return CURLE_SSL_ENGINE_INITFAILED; + failf(data, "Failed to find the SSL filter"); + return CURLE_BAD_FUNCTION_ARGUMENT; } cert = SSL_get1_peer_certificate(octx->ssl); diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index 2529a5b8bbf5a3..5d143486d6547a 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -37,6 +37,7 @@ #include "sendf.h" #include "vtls.h" #include "vtls_int.h" +#include "rustls.h" #include "select.h" #include "strerror.h" #include "multiif.h" @@ -570,7 +571,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, break; default: failf(data, "rustls: unsupported minimum TLS version value"); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_BAD_FUNCTION_ARGUMENT; } switch(conn_config->version_max) { @@ -588,7 +589,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, case CURL_SSLVERSION_MAX_TLSv1_0: default: failf(data, "rustls: unsupported maximum TLS version value"); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_BAD_FUNCTION_ARGUMENT; } cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len)); @@ -610,7 +611,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to create crypto provider builder from default"); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CIPHER; } result = @@ -622,7 +623,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, failf(data, "rustls: failed to set ciphersuites for crypto provider builder"); rustls_crypto_provider_builder_free(custom_provider_builder); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CIPHER; } result = rustls_crypto_provider_builder_build( @@ -630,7 +631,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to build custom crypto provider"); rustls_crypto_provider_builder_free(custom_provider_builder); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CIPHER; } result = rustls_client_config_builder_new_custom(custom_provider, @@ -640,7 +641,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, free(cipher_suites); if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to create client config"); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CIPHER; } } @@ -747,7 +748,7 @@ cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, if(result != RUSTLS_RESULT_OK) { failf(data, "rustls: failed to build client config"); rustls_client_config_free(backend->config); - return CURLE_SSL_ENGINE_INITFAILED; + return CURLE_SSL_CONNECT_ERROR; } DEBUGASSERT(rconn == NULL); @@ -768,11 +769,12 @@ static void cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, const struct rustls_connection *rconn) { + struct ssl_connect_data *const connssl = cf->ctx; const uint8_t *protocol = NULL; size_t len = 0; rustls_connection_get_alpn_protocol(rconn, &protocol, &len); - Curl_alpn_set_negotiated(cf, data, protocol, len); + Curl_alpn_set_negotiated(cf, data, connssl, protocol, len); } /* Given an established network connection, do a TLS handshake. @@ -894,7 +896,7 @@ cr_connect_common(struct Curl_cfilter *cf, } if(0 == what) { CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block", - wants_read&&wants_write ? "writing and reading" : + wants_read && wants_write ? "writing and reading" : wants_write ? "writing" : "reading"); if(wants_write) connssl->io_need |= CURL_SSL_IO_NEED_SEND; diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index f294a1ae6aa554..2085f7094673c3 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -59,6 +59,17 @@ #include "curl_memory.h" #include "memdebug.h" +/* Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF() + * and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These + * messages are extra verbose and intended for curl developers debugging + * Schannel recv decryption. + */ +#ifdef CURL_SCHANNEL_DEV_DEBUG +#define SCH_DEV(x) x +#else +#define SCH_DEV(x) do { } while(0) +#endif + /* ALPN requires version 8.1 of the Windows SDK, which was shipped with Visual Studio 2013, aka _MSC_VER 1800: @@ -1130,7 +1141,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(ssl_config->primary.cache_session) { Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - (void **)&old_cred, NULL)) { + (void **)&old_cred, NULL, NULL)) { backend->cred = old_cred; DEBUGF(infof(data, "schannel: reusing existing credential handle")); @@ -1408,12 +1419,12 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* increase encrypted data buffer offset */ backend->encdata_offset += nread; backend->encdata_is_incomplete = FALSE; - DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); + SCH_DEV(infof(data, "schannel: encrypted data got %zd", nread)); } - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, + "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); /* setup input buffers */ InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(backend->encdata_offset), @@ -1527,8 +1538,8 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* check if there was additional remaining encrypted data */ if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: encrypted data length: %lu", - inbuf[1].cbBuffer)); + SCH_DEV(infof(data, "schannel: encrypted data length: %lu", + inbuf[1].cbBuffer)); /* There are two cases where we could be getting extra data here: 1) If we are renegotiating a connection and the handshake is already @@ -1752,7 +1763,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) SecApplicationProtocolNegotiationStatus_Success) { unsigned char prev_alpn = cf->conn->alpn; - Curl_alpn_set_negotiated(cf, data, alpn_result.ProtocolId, + Curl_alpn_set_negotiated(cf, data, connssl, alpn_result.ProtocolId, alpn_result.ProtocolIdSize); if(backend->recv_renegotiating) { if(prev_alpn != cf->conn->alpn && @@ -1766,7 +1777,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) } else { if(!backend->recv_renegotiating) - Curl_alpn_set_negotiated(cf, data, NULL, 0); + Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0); } } #endif @@ -1776,7 +1787,8 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_ssl_sessionid_lock(data); /* Up ref count since call takes ownership */ backend->cred->refcount++; - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, backend->cred, + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + backend->cred, sizeof(struct Curl_schannel_cred), schannel_session_free); Curl_ssl_sessionid_unlock(data); @@ -2109,17 +2121,23 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, * cleanup. The pattern for return error is set *err, optional infof, goto * cleanup. * + * Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF() + * and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These + * messages are extra verbose and intended for curl developers debugging + * Schannel recv decryption. + * * Our priority is to always return as much decrypted data to the caller as * possible, even if an error occurs. The state of the decrypted buffer must * always be valid. Transfer of decrypted data to the caller's buffer is * handled in the cleanup. */ - DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len)); + SCH_DEV(infof(data, "schannel: client wants to read %zu bytes", len)); *err = CURLE_OK; if(len && len <= backend->decdata_offset) { - infof(data, "schannel: enough decrypted data is already available"); + SCH_DEV(infof(data, + "schannel: enough decrypted data is already available")); goto cleanup; } else if(backend->recv_unrecoverable_err) { @@ -2157,13 +2175,13 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->encdata_buffer = reallocated_buffer; backend->encdata_length = reallocated_length; size = backend->encdata_length - backend->encdata_offset; - DEBUGF(infof(data, "schannel: encdata_buffer resized %zu", - backend->encdata_length)); + SCH_DEV(infof(data, "schannel: encdata_buffer resized %zu", + backend->encdata_length)); } - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, + "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); /* read encrypted data from socket */ nread = Curl_conn_cf_recv(cf->next, data, @@ -2173,8 +2191,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, if(*err) { nread = -1; if(*err == CURLE_AGAIN) - DEBUGF(infof(data, - "schannel: recv returned CURLE_AGAIN")); + SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN")); else if(*err == CURLE_RECV_ERROR) infof(data, "schannel: recv returned CURLE_RECV_ERROR"); else @@ -2187,13 +2204,12 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, else if(nread > 0) { backend->encdata_offset += (size_t)nread; backend->encdata_is_incomplete = FALSE; - DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); + SCH_DEV(infof(data, "schannel: encrypted data got %zd", nread)); } } - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); /* decrypt loop */ while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK && @@ -2221,8 +2237,8 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* check for successfully decrypted data, even before actual renegotiation or shutdown of the connection context */ if(inbuf[1].BufferType == SECBUFFER_DATA) { - DEBUGF(infof(data, "schannel: decrypted data length: %lu", - inbuf[1].cbBuffer)); + SCH_DEV(infof(data, "schannel: decrypted data length: %lu", + inbuf[1].cbBuffer)); /* increase buffer in order to fit the received amount of data */ size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? @@ -2254,16 +2270,16 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->decdata_offset += size; } - DEBUGF(infof(data, "schannel: decrypted data added: %zu", size)); - DEBUGF(infof(data, - "schannel: decrypted cached: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + SCH_DEV(infof(data, "schannel: decrypted data added: %zu", size)); + SCH_DEV(infof(data, + "schannel: decrypted cached: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); } /* check for remaining encrypted data */ if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: encrypted data length: %lu", - inbuf[3].cbBuffer)); + SCH_DEV(infof(data, "schannel: encrypted data length: %lu", + inbuf[3].cbBuffer)); /* check if the remaining data is less than the total amount * and therefore begins after the already processed data @@ -2277,9 +2293,9 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->encdata_offset = inbuf[3].cbBuffer; } - DEBUGF(infof(data, - "schannel: encrypted cached: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, + "schannel: encrypted cached: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); } else { /* reset encrypted buffer offset, because there is no data remaining */ @@ -2318,6 +2334,11 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->recv_sspi_close_notify = TRUE; if(!backend->recv_connection_closed) backend->recv_connection_closed = TRUE; + /* We received the close notify just fine, any error we got + * from the lower filters afterwards (e.g. the socket), is not + * an error on the TLS data stream. That one ended here. */ + if(*err == CURLE_RECV_ERROR) + *err = CURLE_OK; infof(data, "schannel: server close notification received (close_notify)"); goto cleanup; @@ -2327,13 +2348,13 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->encdata_is_incomplete = TRUE; if(!*err) *err = CURLE_AGAIN; - infof(data, "schannel: failed to decrypt data, need more data"); + SCH_DEV(infof(data, "schannel: failed to decrypt data, need more data")); goto cleanup; } else { #ifndef CURL_DISABLE_VERBOSE_STRINGS char buffer[STRERROR_LEN]; - infof(data, "schannel: failed to read data from server: %s", + failf(data, "schannel: failed to read data from server: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); #endif *err = CURLE_RECV_ERROR; @@ -2341,17 +2362,15 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } } - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); + SCH_DEV(infof(data, "schannel: encrypted data buffer: offset %zu length %zu", + backend->encdata_offset, backend->encdata_length)); - DEBUGF(infof(data, - "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + SCH_DEV(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); cleanup: /* Warning- there is no guarantee the encdata state is valid at this point */ - DEBUGF(infof(data, "schannel: schannel_recv cleanup")); + SCH_DEV(infof(data, "schannel: schannel_recv cleanup")); /* Error if the connection has closed without a close_notify. @@ -2373,7 +2392,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->recv_sspi_close_notify = TRUE; else { *err = CURLE_RECV_ERROR; - infof(data, "schannel: server closed abruptly (missing close_notify)"); + failf(data, "schannel: server closed abruptly (missing close_notify)"); } } @@ -2387,10 +2406,10 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, memmove(backend->decdata_buffer, backend->decdata_buffer + size, backend->decdata_offset - size); backend->decdata_offset -= size; - DEBUGF(infof(data, "schannel: decrypted data returned %zu", size)); - DEBUGF(infof(data, - "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); + SCH_DEV(infof(data, "schannel: decrypted data returned %zu", size)); + SCH_DEV(infof(data, + "schannel: decrypted data buffer: offset %zu length %zu", + backend->decdata_offset, backend->decdata_length)); *err = CURLE_OK; return (ssize_t)size; } @@ -2536,7 +2555,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, if(!result) { if(written < (ssize_t)outbuf.cbBuffer) { /* TODO: handle partial sends */ - infof(data, "schannel: failed to send close msg: %s" + failf(data, "schannel: failed to send close msg: %s" " (bytes written: %zd)", curl_easy_strerror(result), written); result = CURLE_SEND_ERROR; goto out; @@ -2551,7 +2570,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, } else { if(!backend->recv_connection_closed) { - infof(data, "schannel: error sending close msg: %d", result); + failf(data, "schannel: error sending close msg: %d", result); result = CURLE_SEND_ERROR; goto out; } diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h index 026a1dbac32cc1..81476bc6d817cb 100644 --- a/lib/vtls/schannel_int.h +++ b/lib/vtls/schannel_int.h @@ -176,6 +176,17 @@ struct schannel_cert_share { struct curltime time; /* when the cached store was created */ }; +/* +* size of the structure: 20 bytes. +*/ +struct num_ip_data { + DWORD size; /* 04 bytes */ + union { + struct in_addr ia; /* 04 bytes */ + struct in6_addr ia6; /* 16 bytes */ + } bData; +}; + HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf, const struct Curl_easy *data); diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 9a166c28178743..4b52b8e8a499cd 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -39,6 +39,7 @@ #include "schannel.h" #include "schannel_int.h" +#include "inet_pton.h" #include "vtls.h" #include "vtls_int.h" #include "sendf.h" @@ -54,7 +55,6 @@ #define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend) - #ifdef HAS_MANUAL_VERIFY_API #define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ @@ -343,7 +343,9 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, static DWORD cert_get_name_string(struct Curl_easy *data, CERT_CONTEXT *cert_context, LPTSTR host_names, - DWORD length) + DWORD length, + PCERT_ALT_NAME_INFO alt_name_info, + BOOL Win8_compat) { DWORD actual_length = 0; #if defined(CURL_WINDOWS_UWP) @@ -351,21 +353,16 @@ static DWORD cert_get_name_string(struct Curl_easy *data, (void)cert_context; (void)host_names; (void)length; + (void)alt_name_info; + (void)Win8_compat; #else BOOL compute_content = FALSE; - CERT_INFO *cert_info = NULL; - CERT_EXTENSION *extension = NULL; - CRYPT_DECODE_PARA decode_para = {0, 0, 0}; - CERT_ALT_NAME_INFO *alt_name_info = NULL; - DWORD alt_name_info_size = 0; - BOOL ret_val = FALSE; LPTSTR current_pos = NULL; DWORD i; #ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ - if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { + if(Win8_compat) { /* CertGetNameString will provide the 8-bit character string without * any decoding */ DWORD name_flags = @@ -378,6 +375,9 @@ static DWORD cert_get_name_string(struct Curl_easy *data, length); return actual_length; } +#else + (void)cert_context; + (void)Win8_compat; #endif compute_content = host_names != NULL && length != 0; @@ -388,43 +388,6 @@ static DWORD cert_get_name_string(struct Curl_easy *data, *host_names = '\0'; } - if(!cert_context) { - failf(data, "schannel: Null certificate context."); - return actual_length; - } - - cert_info = cert_context->pCertInfo; - if(!cert_info) { - failf(data, "schannel: Null certificate info."); - return actual_length; - } - - extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, - cert_info->cExtension, - cert_info->rgExtension); - if(!extension) { - failf(data, "schannel: CertFindExtension() returned no extension."); - return actual_length; - } - - decode_para.cbSize = sizeof(CRYPT_DECODE_PARA); - - ret_val = - CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - szOID_SUBJECT_ALT_NAME2, - extension->Value.pbData, - extension->Value.cbData, - CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, - &decode_para, - &alt_name_info, - &alt_name_info_size); - if(!ret_val) { - failf(data, - "schannel: CryptDecodeObjectEx() returned no alternate name " - "information."); - return actual_length; - } - current_pos = host_names; /* Iterate over the alternate names and populate host_names. */ @@ -467,6 +430,88 @@ static DWORD cert_get_name_string(struct Curl_easy *data, return actual_length; } +/* +* Returns TRUE if the hostname is a numeric IPv4/IPv6 Address, +* and populates the buffer with IPv4/IPv6 info. +*/ + +static bool get_num_host_info(struct num_ip_data *ip_blob, + LPCSTR hostname) +{ + struct in_addr ia; + struct in6_addr ia6; + bool result = FALSE; + + int res = Curl_inet_pton(AF_INET, hostname, &ia); + if(res) { + ip_blob->size = sizeof(struct in_addr); + memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr)); + result = TRUE; + } + else { + res = Curl_inet_pton(AF_INET6, hostname, &ia6); + if(res) { + ip_blob->size = sizeof(struct in6_addr); + memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr)); + result = TRUE; + } + } + return result; +} + +static bool get_alt_name_info(struct Curl_easy *data, + PCCERT_CONTEXT ctx, + PCERT_ALT_NAME_INFO *alt_name_info, + LPDWORD alt_name_info_size) +{ + bool result = FALSE; +#if defined(CURL_WINDOWS_UWP) + (void)data; + (void)ctx; + (void)alt_name_info; + (void)alt_name_info_size; +#else + PCERT_INFO cert_info = NULL; + PCERT_EXTENSION extension = NULL; + CRYPT_DECODE_PARA decode_para = { sizeof(CRYPT_DECODE_PARA), NULL, NULL }; + + if(!ctx) { + failf(data, "schannel: Null certificate context."); + return result; + } + + cert_info = ctx->pCertInfo; + if(!cert_info) { + failf(data, "schannel: Null certificate info."); + return result; + } + + extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, + cert_info->cExtension, + cert_info->rgExtension); + if(!extension) { + failf(data, "schannel: CertFindExtension() returned no extension."); + return result; + } + + if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + szOID_SUBJECT_ALT_NAME2, + extension->Value.pbData, + extension->Value.cbData, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + alt_name_info, + alt_name_info_size)) { + failf(data, + "schannel: CryptDecodeObjectEx() returned no alternate name " + "information."); + return result; + } + result = TRUE; +#endif + return result; +} + /* Verify the server's hostname */ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -481,6 +526,12 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; + PCERT_ALT_NAME_INFO alt_name_info = NULL; + DWORD alt_name_info_size = 0; + struct num_ip_data ip_blob = { 0 }; + bool Win8_compat; + struct num_ip_data *p = &ip_blob; + DWORD i; sspi_status = Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, @@ -491,97 +542,123 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, char buffer[STRERROR_LEN]; failf(data, "schannel: Failed to read remote certificate context: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - result = CURLE_PEER_FAILED_VERIFICATION; goto cleanup; } - /* Determine the size of the string needed for the cert hostname */ - len = cert_get_name_string(data, pCertContextServer, NULL, 0); - if(len == 0) { - failf(data, - "schannel: CertGetNameString() returned no " - "certificate name information"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto cleanup; + Win8_compat = curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL); + if(get_num_host_info(p, conn_hostname) || !Win8_compat) { + if(!get_alt_name_info(data, pCertContextServer, + &alt_name_info, &alt_name_info_size)) { + goto cleanup; + } } - /* CertGetNameString guarantees that the returned name will not contain - * embedded null bytes. This appears to be undocumented behavior. - */ - cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); - if(!cert_hostname_buff) { - result = CURLE_OUT_OF_MEMORY; - goto cleanup; + if(p->size) { + for(i = 0; i < alt_name_info->cAltEntry; ++i) { + PCERT_ALT_NAME_ENTRY entry = &alt_name_info->rgAltEntry[i]; + if(entry->dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) { + if(entry->IPAddress.cbData == p->size) { + if(!memcmp(entry->IPAddress.pbData, &p->bData, + entry->IPAddress.cbData)) { + result = CURLE_OK; + infof(data, + "schannel: connection hostname (%s) matched cert's IP address!", + conn_hostname); + break; + } + } + } + } } - actual_len = cert_get_name_string( - data, pCertContextServer, (LPTSTR)cert_hostname_buff, len); - /* Sanity check */ - if(actual_len != len) { - failf(data, - "schannel: CertGetNameString() returned certificate " - "name information of unexpected size"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto cleanup; - } + else { + /* Determine the size of the string needed for the cert hostname */ + len = cert_get_name_string(data, pCertContextServer, + NULL, 0, alt_name_info, Win8_compat); + if(len == 0) { + failf(data, + "schannel: CertGetNameString() returned no " + "certificate name information"); + goto cleanup; + } - /* cert_hostname_buff contains all DNS names, where each name is - * null-terminated and the last DNS name is double null-terminated. Due to - * this encoding, use the length of the buffer to iterate over all names. - */ - result = CURLE_PEER_FAILED_VERIFICATION; - while(cert_hostname_buff_index < len && - cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && - result == CURLE_PEER_FAILED_VERIFICATION) { + /* CertGetNameString guarantees that the returned name will not contain + * embedded null bytes. This appears to be undocumented behavior. + */ + cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); + if(!cert_hostname_buff) { + result = CURLE_OUT_OF_MEMORY; + goto cleanup; + } + actual_len = cert_get_name_string(data, pCertContextServer, + (LPTSTR)cert_hostname_buff, len, alt_name_info, Win8_compat); - char *cert_hostname; + /* Sanity check */ + if(actual_len != len) { + failf(data, + "schannel: CertGetNameString() returned certificate " + "name information of unexpected size"); + goto cleanup; + } - /* Comparing the cert name and the connection hostname encoded as UTF-8 - * is acceptable since both values are assumed to use ASCII - * (or some equivalent) encoding + /* cert_hostname_buff contains all DNS names, where each name is + * null-terminated and the last DNS name is double null-terminated. Due to + * this encoding, use the length of the buffer to iterate over all names. */ - cert_hostname = curlx_convert_tchar_to_UTF8( + while(cert_hostname_buff_index < len && + cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && + result == CURLE_PEER_FAILED_VERIFICATION) { + + char *cert_hostname; + + /* Comparing the cert name and the connection hostname encoded as UTF-8 + * is acceptable since both values are assumed to use ASCII + * (or some equivalent) encoding + */ + cert_hostname = curlx_convert_tchar_to_UTF8( &cert_hostname_buff[cert_hostname_buff_index]); - if(!cert_hostname) { - result = CURLE_OUT_OF_MEMORY; - } - else { - if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), - conn_hostname, hostlen)) { - infof(data, - "schannel: connection hostname (%s) validated " - "against certificate name (%s)", - conn_hostname, cert_hostname); - result = CURLE_OK; + if(!cert_hostname) { + result = CURLE_OUT_OF_MEMORY; } else { - size_t cert_hostname_len; + if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), + conn_hostname, hostlen)) { + infof(data, + "schannel: connection hostname (%s) validated " + "against certificate name (%s)", + conn_hostname, cert_hostname); + result = CURLE_OK; + } + else { + size_t cert_hostname_len; - infof(data, - "schannel: connection hostname (%s) did not match " - "against certificate name (%s)", - conn_hostname, cert_hostname); + infof(data, + "schannel: connection hostname (%s) did not match " + "against certificate name (%s)", + conn_hostname, cert_hostname); - cert_hostname_len = - _tcslen(&cert_hostname_buff[cert_hostname_buff_index]); + cert_hostname_len = + _tcslen(&cert_hostname_buff[cert_hostname_buff_index]); - /* Move on to next cert name */ - cert_hostname_buff_index += cert_hostname_len + 1; + /* Move on to next cert name */ + cert_hostname_buff_index += cert_hostname_len + 1; - result = CURLE_PEER_FAILED_VERIFICATION; + result = CURLE_PEER_FAILED_VERIFICATION; + } + curlx_unicodefree(cert_hostname); } - curlx_unicodefree(cert_hostname); } - } - if(result == CURLE_PEER_FAILED_VERIFICATION) { - failf(data, - "schannel: CertGetNameString() failed to match " - "connection hostname (%s) against server certificate names", - conn_hostname); + if(result == CURLE_PEER_FAILED_VERIFICATION) { + failf(data, + "schannel: CertGetNameString() failed to match " + "connection hostname (%s) against server certificate names", + conn_hostname); + } + else if(result != CURLE_OK) + failf(data, "schannel: server certificate name verification failed"); } - else if(result != CURLE_OK) - failf(data, "schannel: server certificate name verification failed"); cleanup: Curl_safefree(cert_hostname_buff); @@ -592,7 +669,6 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, return result; } - #ifdef HAS_MANUAL_VERIFY_API /* Verify the server's certificate and hostname */ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c index 3632643c706d65..c6a1c73dcf870f 100644 --- a/lib/vtls/sectransp.c +++ b/lib/vtls/sectransp.c @@ -354,8 +354,8 @@ CF_INLINE void GetDarwinVersionNumber(int *major, int *minor) } /* Parse the version: */ - os_version_major = strtok_r(os_version, ".", &tok_buf); - os_version_minor = strtok_r(NULL, ".", &tok_buf); + os_version_major = Curl_strtok_r(os_version, ".", &tok_buf); + os_version_minor = Curl_strtok_r(NULL, ".", &tok_buf); *major = atoi(os_version_major); *minor = atoi(os_version_minor); free(os_version); @@ -1334,7 +1334,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, Curl_ssl_sessionid_lock(data); if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - (void **)&ssl_sessionid, &ssl_sessionid_len)) { + (void **)&ssl_sessionid, &ssl_sessionid_len, + NULL)) { /* we got a session id, use it! */ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); Curl_ssl_sessionid_unlock(data); @@ -1362,8 +1363,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, return CURLE_SSL_CONNECT_ERROR; } - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, ssl_sessionid, - ssl_sessionid_len, + result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL, + ssl_sessionid, ssl_sessionid_len, sectransp_session_free); Curl_ssl_sessionid_unlock(data); if(result) diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 57dba555276cb2..61b407ab22eaf1 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -55,6 +55,16 @@ #include "vtls.h" /* generic SSL protos etc */ #include "vtls_int.h" + +#include "openssl.h" /* OpenSSL versions */ +#include "gtls.h" /* GnuTLS versions */ +#include "wolfssl.h" /* wolfSSL versions */ +#include "schannel.h" /* Schannel SSPI version */ +#include "sectransp.h" /* Secure Transport (Darwin) version */ +#include "mbedtls.h" /* mbedTLS versions */ +#include "bearssl.h" /* BearSSL versions */ +#include "rustls.h" /* Rustls versions */ + #include "slist.h" #include "sendf.h" #include "strcase.h" @@ -259,7 +269,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, return TRUE; } -static void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc) +static void free_primary_ssl_config(struct ssl_primary_config *sslc) { Curl_safefree(sslc->CApath); Curl_safefree(sslc->CAfile); @@ -359,9 +369,9 @@ CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, void Curl_ssl_conn_config_cleanup(struct connectdata *conn) { - Curl_free_primary_ssl_config(&conn->ssl_config); + free_primary_ssl_config(&conn->ssl_config); #ifndef CURL_DISABLE_PROXY - Curl_free_primary_ssl_config(&conn->proxy_ssl_config); + free_primary_ssl_config(&conn->proxy_ssl_config); #endif } @@ -454,6 +464,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, return NULL; ctx->alpn = alpn; + Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES); ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data); if(!ctx->backend) { free(ctx); @@ -465,6 +476,8 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, static void cf_ctx_free(struct ssl_connect_data *ctx) { if(ctx) { + Curl_safefree(ctx->alpn_negotiated); + Curl_bufq_free(&ctx->earlydata); free(ctx->backend); free(ctx); } @@ -527,7 +540,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, const struct ssl_peer *peer, void **ssl_sessionid, - size_t *idsize) /* set 0 if unknown */ + size_t *idsize, /* set 0 if unknown */ + char **palpn) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); @@ -537,6 +551,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, bool no_match = TRUE; *ssl_sessionid = NULL; + if(palpn) + *palpn = NULL; if(!ssl_config) return TRUE; @@ -575,6 +591,8 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, *ssl_sessionid = check->sessionid; if(idsize) *idsize = check->idsize; + if(palpn) + *palpn = check->alpn; no_match = FALSE; break; } @@ -601,10 +619,11 @@ void Curl_ssl_kill_session(struct Curl_ssl_session *session) session->sessionid_free = NULL; session->age = 0; /* fresh */ - Curl_free_primary_ssl_config(&session->ssl_config); + free_primary_ssl_config(&session->ssl_config); Curl_safefree(session->name); Curl_safefree(session->conn_to_host); + Curl_safefree(session->alpn); } } @@ -628,6 +647,7 @@ void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid) CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, struct Curl_easy *data, const struct ssl_peer *peer, + const char *alpn, void *ssl_sessionid, size_t idsize, Curl_ssl_sessionid_dtor *sessionid_free_cb) @@ -639,6 +659,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, long oldest_age; char *clone_host = NULL; char *clone_conn_to_host = NULL; + char *clone_alpn = NULL; int conn_to_port; long *general_age; void *old_sessionid; @@ -653,7 +674,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, return CURLE_OK; } - if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size)) { + if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size, NULL)) { if((old_size == idsize) && ((old_sessionid == ssl_sessionid) || (idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) { @@ -679,6 +700,10 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, goto out; } + clone_alpn = alpn ? strdup(alpn) : NULL; + if(alpn && !clone_alpn) + goto out; + if(cf->conn->bits.conn_to_port) conn_to_port = cf->conn->conn_to_port; else @@ -711,7 +736,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, /* now init the session struct wisely */ if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) { - Curl_free_primary_ssl_config(&store->ssl_config); + free_primary_ssl_config(&store->ssl_config); store->sessionid = NULL; /* let caller free sessionid */ goto out; } @@ -727,6 +752,8 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */ clone_conn_to_host = NULL; store->conn_to_port = conn_to_port; /* connect to port number */ + store->alpn = clone_alpn; + clone_alpn = NULL; /* port number */ store->remote_port = peer->port; store->scheme = cf->conn->handler->scheme; @@ -737,6 +764,7 @@ CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, out: free(clone_host); free(clone_conn_to_host); + free(clone_alpn); if(result) { failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]", store->scheme, store->name, store->remote_port, @@ -1716,7 +1744,9 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, if(!result && *done) { cf->connected = TRUE; connssl->handshake_done = Curl_now(); - DEBUGASSERT(connssl->state == ssl_connection_complete); + /* Connection can be deferred when sending early data */ + DEBUGASSERT(connssl->state == ssl_connection_complete || + connssl->state == ssl_connection_deferred); } out: CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done); @@ -2033,7 +2063,7 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option) { (void)data; - return (Curl_ssl->supports & ssl_option) ? TRUE : FALSE; + return (Curl_ssl->supports & ssl_option); } static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf) @@ -2219,11 +2249,25 @@ CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, return CURLE_OK; } +bool Curl_alpn_contains_proto(const struct alpn_spec *spec, + const char *proto) +{ + size_t i, plen = proto ? strlen(proto) : 0; + for(i = 0; spec && plen && i < spec->count; ++i) { + size_t slen = strlen(spec->entries[i]); + if((slen == plen) && !memcmp(proto, spec->entries[i], plen)) + return TRUE; + } + return FALSE; +} + CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data, + struct ssl_connect_data *connssl, const unsigned char *proto, size_t proto_len) { + CURLcode result = CURLE_OK; unsigned char *palpn = #ifndef CURL_DISABLE_PROXY (cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf)) ? @@ -2233,6 +2277,45 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, #endif ; + if(connssl->alpn_negotiated) { + /* When we ask for a specific ALPN protocol, we need the confirmation + * of it by the server, as we have installed protocol handler and + * connection filter chain for exactly this protocol. */ + if(!proto_len) { + failf(data, "ALPN: asked for '%s' from previous session, " + "but server did not confirm it. Refusing to continue.", + connssl->alpn_negotiated); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + else if((strlen(connssl->alpn_negotiated) != proto_len) || + memcmp(connssl->alpn_negotiated, proto, proto_len)) { + failf(data, "ALPN: asked for '%s' from previous session, but server " + "selected '%.*s'. Refusing to continue.", + connssl->alpn_negotiated, (int)proto_len, proto); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + /* ALPN is exactly what we asked for, done. */ + infof(data, "ALPN: server confirmed to use '%s'", + connssl->alpn_negotiated); + goto out; + } + + if(proto && proto_len) { + if(memchr(proto, '\0', proto_len)) { + failf(data, "ALPN: server selected protocol contains NUL. " + "Refusing to continue."); + result = CURLE_SSL_CONNECT_ERROR; + goto out; + } + connssl->alpn_negotiated = malloc(proto_len + 1); + if(!connssl->alpn_negotiated) + return CURLE_OUT_OF_MEMORY; + memcpy(connssl->alpn_negotiated, proto, proto_len); + connssl->alpn_negotiated[proto_len] = 0; + } + if(proto && proto_len) { if(proto_len == ALPN_HTTP_1_1_LENGTH && !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) { @@ -2258,15 +2341,22 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, /* return CURLE_NOT_BUILT_IN; */ goto out; } - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, (int)proto_len, proto); + + if(connssl->state == ssl_connection_deferred) + infof(data, VTLS_INFOF_ALPN_DEFERRED, (int)proto_len, proto); + else + infof(data, VTLS_INFOF_ALPN_ACCEPTED, (int)proto_len, proto); } else { *palpn = CURL_HTTP_VERSION_NONE; - infof(data, VTLS_INFOF_NO_ALPN); + if(connssl->state == ssl_connection_deferred) + infof(data, VTLS_INFOF_NO_ALPN_DEFERRED); + else + infof(data, VTLS_INFOF_NO_ALPN); } out: - return CURLE_OK; + return result; } #endif /* USE_SSL */ diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index fce1e001831c22..7a223f6fead32c 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -43,15 +43,18 @@ struct Curl_ssl_session; #define ALPN_ACCEPTED "ALPN: server accepted " -#define VTLS_INFOF_NO_ALPN \ +#define VTLS_INFOF_NO_ALPN \ "ALPN: server did not agree on a protocol. Uses default." -#define VTLS_INFOF_ALPN_OFFER_1STR \ +#define VTLS_INFOF_ALPN_OFFER_1STR \ "ALPN: curl offers %s" -#define VTLS_INFOF_ALPN_ACCEPTED_1STR \ - ALPN_ACCEPTED "%s" -#define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR \ +#define VTLS_INFOF_ALPN_ACCEPTED \ ALPN_ACCEPTED "%.*s" +#define VTLS_INFOF_NO_ALPN_DEFERRED \ + "ALPN: deferred handshake for early data without specific protocol." +#define VTLS_INFOF_ALPN_DEFERRED \ + "ALPN: deferred handshake for early data using '%.*s'." + /* Curl_multi SSL backend-specific data; declared differently by each SSL backend */ struct Curl_cfilter; diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index ce5e7cf3963aa9..13bd3fbb5e71ce 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -29,6 +29,8 @@ #ifdef USE_SSL +struct ssl_connect_data; + /* see https://www.iana.org/assignments/tls-extensiontype-values/ */ #define ALPN_HTTP_1_1_LENGTH 8 #define ALPN_HTTP_1_1 "http/1.1" @@ -61,9 +63,13 @@ CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf, CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data, + struct ssl_connect_data *connssl, const unsigned char *proto, size_t proto_len); +bool Curl_alpn_contains_proto(const struct alpn_spec *spec, + const char *proto); + /* enum for the nonblocking SSL connection state machine */ typedef enum { ssl_connect_1, @@ -74,14 +80,27 @@ typedef enum { typedef enum { ssl_connection_none, + ssl_connection_deferred, ssl_connection_negotiating, ssl_connection_complete } ssl_connection_state; +typedef enum { + ssl_earlydata_none, + ssl_earlydata_use, + ssl_earlydata_sending, + ssl_earlydata_sent, + ssl_earlydata_accepted, + ssl_earlydata_rejected +} ssl_earlydata_state; + #define CURL_SSL_IO_NEED_NONE (0) #define CURL_SSL_IO_NEED_RECV (1<<0) #define CURL_SSL_IO_NEED_SEND (1<<1) +/* Max earlydata payload we want to send */ +#define CURL_SSL_EARLY_MAX (64*1024) + /* Information in each SSL cfilter context: cf->ctx */ struct ssl_connect_data { struct ssl_peer peer; @@ -89,8 +108,14 @@ struct ssl_connect_data { void *backend; /* vtls backend specific props */ struct cf_call_data call_data; /* data handle used in current call */ struct curltime handshake_done; /* time when handshake finished */ + char *alpn_negotiated; /* negotiated ALPN value or NULL */ + struct bufq earlydata; /* earlydata to be send to peer */ + size_t earlydata_max; /* max earlydata allowed by peer */ + size_t earlydata_skip; /* sending bytes to skip when earlydata + * is accepted by peer */ ssl_connection_state state; ssl_connect_state connecting_state; + ssl_earlydata_state earlydata_state; int io_need; /* TLS signals special SEND/RECV needs */ BIT(use_alpn); /* if ALPN shall be used in handshake */ BIT(peer_closed); /* peer has closed connection */ @@ -193,15 +218,23 @@ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf); * Caller must make sure that the ownership of returned sessionid object * is properly taken (e.g. its refcount is incremented * under sessionid mutex). + * @param cf the connection filter wanting to use it + * @param data the transfer involved + * @param peer the peer the filter wants to talk to + * @param sessionid on return the TLS session + * @param idsize on return the size of the TLS session data + * @param palpn on return the ALPN string used by the session, + * set to NULL when not interested */ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, struct Curl_easy *data, const struct ssl_peer *peer, void **ssl_sessionid, - size_t *idsize); /* set 0 if unknown */ + size_t *idsize, /* set 0 if unknown */ + char **palpn); /* Set a TLS session ID for `peer`. Replaces an existing session ID if - * not already the very same. + * not already the same. * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock). * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb` * to deallocate it. Is called in all outcomes, either right away or @@ -212,19 +245,11 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf, CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf, struct Curl_easy *data, const struct ssl_peer *peer, + const char *alpn, void *sessionid, size_t sessionid_size, Curl_ssl_sessionid_dtor *sessionid_free_cb); -#include "openssl.h" /* OpenSSL versions */ -#include "gtls.h" /* GnuTLS versions */ -#include "wolfssl.h" /* wolfSSL versions */ -#include "schannel.h" /* Schannel SSPI version */ -#include "sectransp.h" /* Secure Transport (Darwin) version */ -#include "mbedtls.h" /* mbedTLS versions */ -#include "bearssl.h" /* BearSSL versions */ -#include "rustls.h" /* Rustls versions */ - #endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_INT_H */ diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 896e490110cf44..0d74b3e763dd97 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -97,9 +97,25 @@ #endif #endif -#if defined(HAVE_WOLFSSL_FULL_BIO) && HAVE_WOLFSSL_FULL_BIO +#ifdef HAVE_WOLFSSL_BIO #define USE_BIO_CHAIN -#else +#ifdef HAVE_WOLFSSL_FULL_BIO +#define USE_FULL_BIO +#else /* HAVE_WOLFSSL_FULL_BIO */ +#undef USE_FULL_BIO +#endif +/* wolfSSL 5.7.4 and older do not have these symbols, but only the + * OpenSSL ones. */ +#ifndef WOLFSSL_BIO_CTRL_GET_CLOSE +#define WOLFSSL_BIO_CTRL_GET_CLOSE BIO_CTRL_GET_CLOSE +#define WOLFSSL_BIO_CTRL_SET_CLOSE BIO_CTRL_SET_CLOSE +#define WOLFSSL_BIO_CTRL_FLUSH BIO_CTRL_FLUSH +#define WOLFSSL_BIO_CTRL_DUP BIO_CTRL_DUP +#define wolfSSL_BIO_set_retry_write BIO_set_retry_write +#define wolfSSL_BIO_set_retry_read BIO_set_retry_read +#endif /* !WOLFSSL_BIO_CTRL_GET_CLOSE */ + +#else /* HAVE_WOLFSSL_BIO */ #undef USE_BIO_CHAIN #endif @@ -163,7 +179,7 @@ wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret, #endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */ static void -wolfssl_log_tls12_secret(SSL *ssl) +wolfssl_log_tls12_secret(WOLFSSL *ssl) { unsigned char *ms, *sr, *cr; unsigned int msLen, srLen, crLen, i, x = 0; @@ -187,7 +203,7 @@ wolfssl_log_tls12_secret(SSL *ssl) #endif if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != - SSL_SUCCESS) { + WOLFSSL_SUCCESS) { return; } @@ -208,11 +224,11 @@ wolfssl_log_tls12_secret(SSL *ssl) static int wolfssl_do_file_type(const char *type) { if(!type || !type[0]) - return SSL_FILETYPE_PEM; + return WOLFSSL_FILETYPE_PEM; if(strcasecompare(type, "PEM")) - return SSL_FILETYPE_PEM; + return WOLFSSL_FILETYPE_PEM; if(strcasecompare(type, "DER")) - return SSL_FILETYPE_ASN1; + return WOLFSSL_FILETYPE_ASN1; return -1; } @@ -237,7 +253,9 @@ static const struct group_name_map gnm[] = { static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio) { +#ifdef USE_FULL_BIO wolfSSL_BIO_set_shutdown(bio, 1); +#endif wolfSSL_BIO_set_data(bio, NULL); return 1; } @@ -251,28 +269,35 @@ static int wolfssl_bio_cf_destroy(WOLFSSL_BIO *bio) static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) { - struct Curl_cfilter *cf = BIO_get_data(bio); + struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio); long ret = 1; (void)cf; (void)ptr; + (void)num; switch(cmd) { - case BIO_CTRL_GET_CLOSE: + case WOLFSSL_BIO_CTRL_GET_CLOSE: +#ifdef USE_FULL_BIO ret = (long)wolfSSL_BIO_get_shutdown(bio); +#else + ret = 0; +#endif break; - case BIO_CTRL_SET_CLOSE: + case WOLFSSL_BIO_CTRL_SET_CLOSE: +#ifdef USE_FULL_BIO wolfSSL_BIO_set_shutdown(bio, (int)num); +#endif break; - case BIO_CTRL_FLUSH: + case WOLFSSL_BIO_CTRL_FLUSH: /* we do no delayed writes, but if we ever would, this * needs to trigger it. */ ret = 1; break; - case BIO_CTRL_DUP: + case WOLFSSL_BIO_CTRL_DUP: ret = 1; break; -#ifdef BIO_CTRL_EOF - case BIO_CTRL_EOF: +#ifdef WOLFSSL_BIO_CTRL_EOF + case WOLFSSL_BIO_CTRL_EOF: /* EOF has been reached on input? */ return (!cf->next || !cf->next->connected); #endif @@ -309,9 +334,11 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio, backend->io_result = result; CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d", blen, nwritten, result); +#ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); +#endif if(nwritten < 0 && CURLE_AGAIN == result) { - BIO_set_retry_write(bio); + wolfSSL_BIO_set_retry_write(bio); if(backend->shutting_down && !backend->io_send_blocked_len) backend->io_send_blocked_len = blen; } @@ -338,9 +365,11 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); backend->io_result = result; CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result); +#ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); +#endif if(nread < 0 && CURLE_AGAIN == result) - BIO_set_retry_read(bio); + wolfSSL_BIO_set_retry_read(bio); else if(nread == 0) connssl->peer_closed = TRUE; return (int)nread; @@ -350,7 +379,8 @@ static WOLFSSL_BIO_METHOD *wolfssl_bio_cf_method = NULL; static void wolfssl_bio_cf_init_methods(void) { - wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO"); + wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY, + "wolfSSL CF BIO"); wolfSSL_BIO_meth_set_write(wolfssl_bio_cf_method, &wolfssl_bio_cf_out_write); wolfSSL_BIO_meth_set_read(wolfssl_bio_cf_method, &wolfssl_bio_cf_in_read); wolfSSL_BIO_meth_set_ctrl(wolfssl_bio_cf_method, &wolfssl_bio_cf_ctrl); @@ -370,9 +400,114 @@ static void wolfssl_bio_cf_free_methods(void) #endif /* !USE_BIO_CHAIN */ +static void wolfssl_session_free(void *sdata, size_t slen) +{ + (void)slen; + free(sdata); +} + +CURLcode wssl_cache_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + WOLFSSL_SESSION *session) +{ + CURLcode result = CURLE_OK; + unsigned char *sdata = NULL; + unsigned int slen; + + if(!session) + goto out; + + slen = wolfSSL_i2d_SSL_SESSION(session, NULL); + if(slen <= 0) { + CURL_TRC_CF(data, cf, "fail to assess session length: %u", slen); + result = CURLE_FAILED_INIT; + goto out; + } + sdata = calloc(1, slen); + if(!sdata) { + failf(data, "unable to allocate session buffer of %u bytes", slen); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + slen = wolfSSL_i2d_SSL_SESSION(session, &sdata); + if(slen <= 0) { + CURL_TRC_CF(data, cf, "fail to serialize session: %u", slen); + result = CURLE_FAILED_INIT; + goto out; + } + + Curl_ssl_sessionid_lock(data); + result = Curl_ssl_set_sessionid(cf, data, peer, NULL, + sdata, slen, wolfssl_session_free); + Curl_ssl_sessionid_unlock(data); + if(result) + failf(data, "failed to add new ssl session to cache (%d)", result); + else { + CURL_TRC_CF(data, cf, "added new session to cache"); + sdata = NULL; + } + +out: + free(sdata); + return 0; +} + +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); + if(cf && session) { + struct ssl_connect_data *connssl = cf->ctx; + struct Curl_easy *data = CF_DATA_CURRENT(cf); + DEBUGASSERT(connssl); + DEBUGASSERT(data); + if(connssl && data) { + (void)wssl_cache_session(cf, data, &connssl->peer, session); + } + } + return 0; +} + +CURLcode wssl_setup_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wss, + struct ssl_peer *peer) +{ + void *psdata; + const unsigned char *sdata = NULL; + size_t slen = 0; + CURLcode result = CURLE_OK; + + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(cf, data, peer, &psdata, &slen, NULL)) { + WOLFSSL_SESSION *session; + sdata = psdata; + session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)slen); + if(session) { + int ret = wolfSSL_set_session(wss->handle, session); + if(ret != WOLFSSL_SUCCESS) { + Curl_ssl_delsessionid(data, psdata); + infof(data, "previous session not accepted (%d), " + "removing from cache", ret); + } + else + infof(data, "SSL reusing session ID"); + wolfSSL_SESSION_free(session); + } + else { + failf(data, "could not decode previous session"); + } + } + Curl_ssl_sessionid_unlock(data); + return result; +} + static CURLcode populate_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, - X509_STORE *store, + WOLFSSL_X509_STORE *store, struct wolfssl_ctx *wssl) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); @@ -402,7 +537,8 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, if(ca_info_blob) { if(wolfSSL_CTX_load_verify_buffer(wssl->ctx, ca_info_blob->data, (long)ca_info_blob->len, - SSL_FILETYPE_PEM) != SSL_SUCCESS) { + WOLFSSL_FILETYPE_PEM) != + WOLFSSL_SUCCESS) { if(imported_native_ca) { infof(data, "error importing CA certificate blob, continuing anyway"); } @@ -431,7 +567,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, ssl_cafile, ssl_capath, WOLFSSL_LOAD_FLAG_IGNORE_ERR); - if(SSL_SUCCESS != rc) { + if(WOLFSSL_SUCCESS != rc) { if(conn_config->verifypeer) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:" @@ -509,8 +645,8 @@ cached_x509_store_different(struct Curl_cfilter *cf, return strcmp(mb->CAfile, conn_config->CAfile); } -static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, - const struct Curl_easy *data) +static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) { struct Curl_multi *multi = data->multi; struct wssl_x509_share *share; @@ -531,7 +667,7 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, static void set_cached_x509_store(struct Curl_cfilter *cf, const struct Curl_easy *data, - X509_STORE *store) + WOLFSSL_X509_STORE *store) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct Curl_multi *multi = data->multi; @@ -563,13 +699,13 @@ static void set_cached_x509_store(struct Curl_cfilter *cf, if(conn_config->CAfile) { CAfile = strdup(conn_config->CAfile); if(!CAfile) { - X509_STORE_free(store); + wolfSSL_X509_STORE_free(store); return; } } if(share->store) { - X509_STORE_free(share->store); + wolfSSL_X509_STORE_free(share->store); free(share->CAfile); } @@ -609,7 +745,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, else if(cache_criteria_met) { /* wolfSSL's initial store in CTX is not shareable by default. * Make a new one, suitable for adding to the cache. See #14278 */ - X509_STORE *store = wolfSSL_X509_STORE_new(); + WOLFSSL_X509_STORE *store = wolfSSL_X509_STORE_new(); if(!store) { failf(data, "SSL: could not create a X509 store"); return CURLE_OUT_OF_MEMORY; @@ -623,7 +759,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, } else { /* We never share the CTX's store, use it. */ - X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); + WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx); result = populate_x509_store(cf, data, store, wssl); } @@ -658,7 +794,8 @@ wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) } #endif -#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ +/* 4.2.0 (2019) */ +#if LIBWOLFSSL_VERSION_HEX < 0x04002000 || !defined(OPENSSL_EXTRA) static int wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX* ctx, int version) { @@ -833,7 +970,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(result) return result; - if(!SSL_CTX_set_cipher_list(backend->ctx, Curl_dyn_ptr(&c))) { + if(!wolfSSL_CTX_set_cipher_list(backend->ctx, Curl_dyn_ptr(&c))) { failf(data, "failed setting cipher list: %s", Curl_dyn_ptr(&c)); Curl_dyn_free(&c); return CURLE_SSL_CIPHER; @@ -857,7 +994,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(pqkem == 0) #endif { - if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { + if(!wolfSSL_CTX_set1_curves_list(backend->ctx, curves)) { failf(data, "failed setting curves list: '%s'", curves); return CURLE_SSL_CIPHER; } @@ -958,8 +1095,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ wolfSSL_CTX_set_verify(backend->ctx, - conn_config->verifypeer ? SSL_VERIFY_PEER : - SSL_VERIFY_NONE, NULL); + conn_config->verifypeer ? WOLFSSL_VERIFY_PEER : + WOLFSSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(connssl->peer.sni) { @@ -1026,7 +1163,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) if(result || wolfSSL_UseALPN(backend->handle, (char *)proto.data, (unsigned int)proto.len, - WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) { + WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) { failf(data, "SSL: failed setting ALPN protocols"); return CURLE_SSL_CONNECT_ERROR; } @@ -1054,20 +1191,11 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Check if there is a cached ID we can/should use here! */ if(ssl_config->primary.cache_session) { - void *ssl_sessionid = NULL; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, - &ssl_sessionid, NULL)) { - /* we got a session id, use it! */ - if(!SSL_set_session(backend->handle, ssl_sessionid)) { - Curl_ssl_delsessionid(data, ssl_sessionid); - infof(data, "cannot use session ID, going on without"); - } - else - infof(data, "SSL reusing session ID"); - } - Curl_ssl_sessionid_unlock(data); + /* Set session from cache if there is one */ + (void)wssl_setup_session(cf, data, backend, &connssl->peer); + /* Register to get notified when a new session is received */ + wolfSSL_set_app_data(backend->handle, cf); + wolfSSL_CTX_sess_set_new_cb(backend->ctx, wssl_vtls_new_session_cb); } #ifdef USE_ECH @@ -1150,7 +1278,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { WOLFSSL_BIO *bio; - bio = BIO_new(wolfssl_bio_cf_method); + bio = wolfSSL_BIO_new(wolfssl_bio_cf_method); if(!bio) return CURLE_OUT_OF_MEMORY; @@ -1213,7 +1341,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(conn_config->verifyhost) { char *snihost = connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname; - if(wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE) + if(wolfSSL_check_domain_name(backend->handle, snihost) == WOLFSSL_FAILURE) return CURLE_SSL_CONNECT_ERROR; } @@ -1242,7 +1370,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) * Note that OpenSSL SSL_want_read() is always true here. If wolfSSL ever * changes, the worst case is that no key is logged on error. */ - if(ret == SSL_SUCCESS || + if(ret == WOLFSSL_SUCCESS || (!wolfSSL_want_read(backend->handle) && !wolfSSL_want_write(backend->handle))) { wolfssl_log_tls12_secret(backend->handle); @@ -1256,11 +1384,11 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(ret != 1) { int detail = wolfSSL_get_error(backend->handle, ret); - if(SSL_ERROR_WANT_READ == detail) { + if(WOLFSSL_ERROR_WANT_READ == detail) { connssl->io_need = CURL_SSL_IO_NEED_RECV; return CURLE_OK; } - else if(SSL_ERROR_WANT_WRITE == detail) { + else if(WOLFSSL_ERROR_WANT_WRITE == detail) { connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_OK; } @@ -1352,7 +1480,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) if(pinnedpubkey) { #ifdef KEEP_PEER_CERT - X509 *x509; + WOLFSSL_X509 *x509; const char *x509_der; int x509_der_len; struct Curl_X509certificate x509_parsed; @@ -1404,12 +1532,12 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len); - if(rc == SSL_SUCCESS) { - Curl_alpn_set_negotiated(cf, data, (const unsigned char *)protocol, - protocol_len); + if(rc == WOLFSSL_SUCCESS) { + Curl_alpn_set_negotiated(cf, data, connssl, + (const unsigned char *)protocol, protocol_len); } - else if(rc == SSL_ALPN_NOT_FOUND) - Curl_alpn_set_negotiated(cf, data, NULL, 0); + else if(rc == WOLFSSL_ALPN_NOT_FOUND) + Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0); else { failf(data, "ALPN, failure getting protocol, error %d", rc); return CURLE_SSL_CONNECT_ERROR; @@ -1429,50 +1557,6 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_OK; } - -static void wolfssl_session_free(void *sessionid, size_t idsize) -{ - (void)idsize; - wolfSSL_SESSION_free(sessionid); -} - - -static CURLcode -wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = cf->ctx; - struct wolfssl_ctx *backend = - (struct wolfssl_ctx *)connssl->backend; - const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - DEBUGASSERT(backend); - - if(ssl_config->primary.cache_session) { - /* wolfSSL_get1_session allocates memory that has to be freed. */ - WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle); - - if(our_ssl_sessionid) { - Curl_ssl_sessionid_lock(data); - /* call takes ownership of `our_ssl_sessionid` */ - result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, - our_ssl_sessionid, 0, - wolfssl_session_free); - Curl_ssl_sessionid_unlock(data); - if(result) { - failf(data, "failed to store ssl session"); - return result; - } - } - } - - connssl->connecting_state = ssl_connect_done; - - return result; -} - - static ssize_t wolfssl_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *mem, @@ -1494,8 +1578,8 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf, int err = wolfSSL_get_error(backend->handle, rc); switch(err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: + case WOLFSSL_ERROR_WANT_READ: + case WOLFSSL_ERROR_WANT_WRITE: /* there is data pending, re-invoke SSL_write() */ CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len); *curlcode = CURLE_AGAIN; @@ -1544,14 +1628,14 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, wctx->shutting_down = TRUE; connssl->io_need = CURL_SSL_IO_NEED_NONE; *done = FALSE; - if(!(wolfSSL_get_shutdown(wctx->handle) & SSL_SENT_SHUTDOWN)) { + if(!(wolfSSL_get_shutdown(wctx->handle) & WOLFSSL_SENT_SHUTDOWN)) { /* We have not started the shutdown from our side yet. Check * if the server already sent us one. */ - ERR_clear_error(); + wolfSSL_ERR_clear_error(); nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf)); err = wolfSSL_get_error(wctx->handle, nread); CURL_TRC_CF(data, cf, "wolfSSL_read, nread=%d, err=%d", nread, err); - if(!nread && err == SSL_ERROR_ZERO_RETURN) { + if(!nread && err == WOLFSSL_ERROR_ZERO_RETURN) { bool input_pending; /* Yes, it did. */ if(!send_shutdown) { @@ -1574,13 +1658,13 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, /* SSL should now have started the shutdown from our side. Since it * was not complete, we are lacking the close notify from the server. */ if(send_shutdown) { - ERR_clear_error(); + wolfSSL_ERR_clear_error(); if(wolfSSL_shutdown(wctx->handle) == 1) { CURL_TRC_CF(data, cf, "SSL shutdown finished"); *done = TRUE; goto out; } - if(SSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) { + if(WOLFSSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) { CURL_TRC_CF(data, cf, "SSL shutdown still wants to send"); connssl->io_need = CURL_SSL_IO_NEED_SEND; goto out; @@ -1590,25 +1674,25 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf, } for(i = 0; i < 10; ++i) { - ERR_clear_error(); + wolfSSL_ERR_clear_error(); nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf)); if(nread <= 0) break; } err = wolfSSL_get_error(wctx->handle, nread); switch(err) { - case SSL_ERROR_ZERO_RETURN: /* no more data */ + case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */ CURL_TRC_CF(data, cf, "SSL shutdown received"); *done = TRUE; break; - case SSL_ERROR_NONE: /* just did not get anything */ - case SSL_ERROR_WANT_READ: + case WOLFSSL_ERROR_NONE: /* just did not get anything */ + case WOLFSSL_ERROR_WANT_READ: /* SSL has send its notify and now wants to read the reply * from the server. We are not really interested in that. */ CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive"); connssl->io_need = CURL_SSL_IO_NEED_RECV; break; - case SSL_ERROR_WANT_WRITE: + case WOLFSSL_ERROR_WANT_WRITE: CURL_TRC_CF(data, cf, "SSL shutdown send blocked"); connssl->io_need = CURL_SSL_IO_NEED_SEND; break; @@ -1669,13 +1753,18 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, int err = wolfSSL_get_error(backend->handle, nread); switch(err) { - case SSL_ERROR_ZERO_RETURN: /* no more data */ + case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */ CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); *curlcode = CURLE_OK; return 0; - case SSL_ERROR_NONE: - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: + case WOLFSSL_ERROR_NONE: + case WOLFSSL_ERROR_WANT_READ: + case WOLFSSL_ERROR_WANT_WRITE: + if(!backend->io_result && connssl->peer_closed) { + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); + *curlcode = CURLE_OK; + return 0; + } /* there is data pending, re-invoke wolfSSL_read() */ CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen); *curlcode = CURLE_AGAIN; @@ -1686,7 +1775,12 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, *curlcode = CURLE_AGAIN; return -1; } - { + else if(!backend->io_result && connssl->peer_closed) { + CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen); + *curlcode = CURLE_OK; + return 0; + } + else { char error_buffer[256]; failf(data, "SSL read: %s, errno %d", wolfssl_strerror((unsigned long)err, error_buffer, @@ -1719,7 +1813,7 @@ static int wolfssl_init(void) #ifdef OPENSSL_EXTRA Curl_tls_keylog_open(); #endif - ret = (wolfSSL_Init() == SSL_SUCCESS); + ret = (wolfSSL_Init() == WOLFSSL_SUCCESS); wolfssl_bio_cf_init_methods(); return ret; } @@ -1746,7 +1840,7 @@ static bool wolfssl_data_pending(struct Curl_cfilter *cf, backend = (struct wolfssl_ctx *)ctx->backend; if(backend->handle) /* SSL is in use */ - return (0 != wolfSSL_pending(backend->handle)) ? TRUE : FALSE; + return wolfSSL_pending(backend->handle); else return FALSE; } @@ -1833,9 +1927,9 @@ wolfssl_connect_common(struct Curl_cfilter *cf, } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - result = wolfssl_connect_step3(cf, data); - if(result) - return result; + /* In other backends, this is where we verify the certificate, but + * wolfSSL already does that as part of the handshake. */ + connssl->connecting_state = ssl_connect_done; } if(ssl_connect_done == connssl->connecting_state) { diff --git a/lib/vtls/wolfssl.h b/lib/vtls/wolfssl.h index 318d8b4ab3308b..dc2967d69c7480 100644 --- a/lib/vtls/wolfssl.h +++ b/lib/vtls/wolfssl.h @@ -26,13 +26,16 @@ #include "curl_setup.h" #ifdef USE_WOLFSSL -#include -#include -#include -#include #include "urldata.h" +struct WOLFSSL; +typedef struct WOLFSSL WOLFSSL; +struct WOLFSSL_CTX; +typedef struct WOLFSSL_CTX WOLFSSL_CTX; +struct WOLFSSL_SESSION; +typedef struct WOLFSSL_SESSION WOLFSSL_SESSION; + extern const struct Curl_ssl Curl_ssl_wolfssl; struct wolfssl_ctx { @@ -48,5 +51,16 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, struct wolfssl_ctx *wssl); +CURLcode wssl_setup_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct wolfssl_ctx *wss, + struct ssl_peer *peer); + +CURLcode wssl_cache_session(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + WOLFSSL_SESSION *session); + + #endif /* USE_WOLFSSL */ #endif /* HEADER_CURL_WOLFSSL_H */ diff --git a/lib/warnless.c b/lib/warnless.c index c80937b843d3e1..8da3be3a8ae32e 100644 --- a/lib/warnless.c +++ b/lib/warnless.c @@ -345,28 +345,6 @@ size_t curlx_sitouz(int sinum) #endif } -#ifdef USE_WINSOCK - -/* -** curl_socket_t to signed int -*/ - -int curlx_sktosi(curl_socket_t s) -{ - return (int)((ssize_t) s); -} - -/* -** signed int to curl_socket_t -*/ - -curl_socket_t curlx_sitosk(int i) -{ - return (curl_socket_t)((ssize_t) i); -} - -#endif /* USE_WINSOCK */ - #if defined(_WIN32) ssize_t curlx_read(int fd, void *buf, size_t count) diff --git a/lib/warnless.h b/lib/warnless.h index 6adf63a793efd1..972c7a9c0d9b9f 100644 --- a/lib/warnless.h +++ b/lib/warnless.h @@ -61,14 +61,6 @@ unsigned short curlx_uitous(unsigned int uinum); size_t curlx_sitouz(int sinum); -#ifdef USE_WINSOCK - -int curlx_sktosi(curl_socket_t s); - -curl_socket_t curlx_sitosk(int i); - -#endif /* USE_WINSOCK */ - #if defined(_WIN32) ssize_t curlx_read(int fd, void *buf, size_t count); diff --git a/lib/ws.c b/lib/ws.c index 8b58f1afc6496d..3d739a538a1742 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -924,14 +924,18 @@ static ssize_t nw_in_recv(void *reader_ctx, return (ssize_t)nread; } -CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, +CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer, size_t buflen, size_t *nread, const struct curl_ws_frame **metap) { + struct Curl_easy *data = d; struct connectdata *conn = data->conn; struct websocket *ws; struct ws_collect ctx; + *nread = 0; + *metap = NULL; + if(!conn) { /* Unhappy hack with lifetimes of transfers and connection */ if(!data->set.connect_only) { @@ -951,8 +955,6 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, return CURLE_BAD_FUNCTION_ARGUMENT; } - *nread = 0; - *metap = NULL; memset(&ctx, 0, sizeof(ctx)); ctx.data = data; @@ -1047,11 +1049,12 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, return CURLE_OK; } -static CURLcode ws_send_raw_blocking(CURL *data, struct websocket *ws, +static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws, const char *buffer, size_t buflen) { CURLcode result = CURLE_OK; size_t nwritten; + struct Curl_easy *data = d; (void)ws; while(buflen) { @@ -1088,7 +1091,7 @@ static CURLcode ws_send_raw_blocking(CURL *data, struct websocket *ws, return result; } -static CURLcode ws_send_raw(CURL *data, const void *buffer, +static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, size_t buflen, size_t *pnwritten) { struct websocket *ws = data->conn->proto.ws; @@ -1124,7 +1127,7 @@ static CURLcode ws_send_raw(CURL *data, const void *buffer, return result; } -CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, +CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags) @@ -1133,6 +1136,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer, ssize_t n; size_t space, payload_added; CURLcode result; + struct Curl_easy *data = d; CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T ", flags=%x), raw=%d", @@ -1289,10 +1293,11 @@ static CURLcode ws_disconnect(struct Curl_easy *data, return CURLE_OK; } -CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) +CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *d) { /* we only return something for websocket, called from within the callback when not using raw mode */ + struct Curl_easy *data = d; if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn && data->conn->proto.ws && !data->set.ws_raw_mode) return &data->conn->proto.ws->frame; @@ -1380,7 +1385,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, return CURLE_NOT_BUILT_IN; } -CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data) +CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *data) { (void)data; return NULL; diff --git a/libcurl.pc.in b/libcurl.pc.in index 4c60a7ec76a6e2..c0ba5244a83990 100644 --- a/libcurl.pc.in +++ b/libcurl.pc.in @@ -36,6 +36,6 @@ Version: @CURLVERSION@ Requires: @LIBCURL_PC_REQUIRES@ Requires.private: @LIBCURL_PC_REQUIRES_PRIVATE@ Libs: -L${libdir} -lcurl @LIBCURL_PC_LIBS@ -Libs.private: @LDFLAGS@ @LIBCURL_PC_LIBS_PRIVATE@ +Libs.private: @LIBCURL_PC_LDFLAGS_PRIVATE@ @LIBCURL_PC_LIBS_PRIVATE@ Cflags: -I${includedir} @LIBCURL_PC_CFLAGS@ Cflags.private: @LIBCURL_PC_CFLAGS_PRIVATE@ diff --git a/m4/curl-bearssl.m4 b/m4/curl-bearssl.m4 index 02a80e8c13f5fb..b16e59d031d7f0 100644 --- a/m4/curl-bearssl.m4 +++ b/m4/curl-bearssl.m4 @@ -30,6 +30,7 @@ dnl ---------------------------------------------------- if test "x$OPT_BEARSSL" != xno; then _cppflags=$CPPFLAGS _ldflags=$LDFLAGS + _ldflagspc=$LDFLAGSPC ssl_msg= if test X"$OPT_BEARSSL" != Xno; then @@ -65,6 +66,7 @@ if test "x$OPT_BEARSSL" != xno; then bearssllib=$OPT_BEARSSL/lib$libsuff LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" if test "$addcflags" != "-I/usr/include"; then CPPFLAGS="$CPPFLAGS $addcflags" fi @@ -81,6 +83,7 @@ if test "x$OPT_BEARSSL" != xno; then [ CPPFLAGS=$_cppflags LDFLAGS=$_ldflags + LDFLAGSPC=$_ldflagspc ], -lbearssl) fi diff --git a/m4/curl-compilers.m4 b/m4/curl-compilers.m4 index c70cbf2fd7a4b5..787423d1ff3e4c 100644 --- a/m4/curl-compilers.m4 +++ b/m4/curl-compilers.m4 @@ -879,7 +879,7 @@ AC_DEFUN([CURL_SET_COMPILER_WARNING_OPTS], [ dnl Only clang 3.5 or later if test "$compiler_num" -ge "305"; then CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [pragmas]) - CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unreachable-code-break]) + # CURL_ADD_COMPILER_WARNINGS([tmp_CFLAGS], [unreachable-code-break]) # Not used: Silent in "unity" builds fi # dnl Only clang 3.6 or later @@ -1379,7 +1379,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_SYMBOL_HIDING], [ case "$compiler_id" in CLANG|APPLECLANG) dnl All versions of clang support -fvisibility= - tmp_EXTERN="__attribute__ ((__visibility__ (\"default\")))" + tmp_EXTERN="__attribute__((__visibility__(\"default\")))" tmp_CFLAGS="-fvisibility=hidden" supports_symbol_hiding="yes" ;; @@ -1387,7 +1387,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_SYMBOL_HIDING], [ dnl Only gcc 3.4 or later if test "$compiler_num" -ge "304"; then if $CC --help --verbose 2>/dev/null | grep fvisibility= >/dev/null ; then - tmp_EXTERN="__attribute__ ((__visibility__ (\"default\")))" + tmp_EXTERN="__attribute__((__visibility__(\"default\")))" tmp_CFLAGS="-fvisibility=hidden" supports_symbol_hiding="yes" fi @@ -1406,7 +1406,7 @@ AC_DEFUN([CURL_CHECK_COMPILER_SYMBOL_HIDING], [ printf("icc fvisibility bug test"); ]]) ],[ - tmp_EXTERN="__attribute__ ((__visibility__ (\"default\")))" + tmp_EXTERN="__attribute__((__visibility__(\"default\")))" tmp_CFLAGS="-fvisibility=hidden" supports_symbol_hiding="yes" ]) diff --git a/m4/curl-confopts.m4 b/m4/curl-confopts.m4 index 8109b76554b2fc..507c0819208119 100644 --- a/m4/curl-confopts.m4 +++ b/m4/curl-confopts.m4 @@ -484,6 +484,7 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [ dnl c-ares library support has been requested clean_CPPFLAGS="$CPPFLAGS" clean_LDFLAGS="$LDFLAGS" + clean_LDFLAGSPC="$LDFLAGSPC" clean_LIBS="$LIBS" configure_runpath=`pwd` if test -n "$want_ares_path"; then @@ -525,6 +526,7 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [ # CPPFLAGS="$clean_CPPFLAGS $ares_CPPFLAGS" LDFLAGS="$clean_LDFLAGS $ares_LDFLAGS" + LDFLAGSPC="$clean_LDFLAGSPC $ares_LDFLAGS" LIBS="$ares_LIBS $clean_LIBS" # @@ -553,6 +555,7 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [ dnl restore initial settings CPPFLAGS="$clean_CPPFLAGS" LDFLAGS="$clean_LDFLAGS" + LDFLAGSPC="$clean_LDFLAGSPC" LIBS="$clean_LIBS" # prevent usage want_ares="no" diff --git a/m4/curl-functions.m4 b/m4/curl-functions.m4 index fd4aac336b6244..356c6651e5f8c2 100644 --- a/m4/curl-functions.m4 +++ b/m4/curl-functions.m4 @@ -150,30 +150,6 @@ curl_includes_netdb="\ ]) -dnl CURL_INCLUDES_POLL -dnl ------------------------------------------------- -dnl Set up variable with list of headers that must be -dnl included when poll.h is to be included. - -AC_DEFUN([CURL_INCLUDES_POLL], [ -curl_includes_poll="\ -/* includes start */ -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_POLL_H -# include -#endif -#ifdef HAVE_SYS_POLL_H -# include -#endif -/* includes end */" - AC_CHECK_HEADERS( - sys/types.h poll.h sys/poll.h, - [], [], [$curl_includes_poll]) -]) - - dnl CURL_INCLUDES_SETJMP dnl ------------------------------------------------- dnl Set up variable with list of headers that must be @@ -212,27 +188,6 @@ curl_includes_signal="\ ]) -dnl CURL_INCLUDES_SOCKET -dnl ------------------------------------------------- -dnl Set up variable with list of headers that must be -dnl included when socket.h is to be included. - -AC_DEFUN([CURL_INCLUDES_SOCKET], [ -curl_includes_socket="\ -/* includes start */ -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SOCKET_H -# include -#endif -/* includes end */" - AC_CHECK_HEADERS( - sys/types.h socket.h, - [], [], [$curl_includes_socket]) -]) - - dnl CURL_INCLUDES_STDLIB dnl ------------------------------------------------- dnl Set up variable with list of headers that must be @@ -694,7 +649,6 @@ dnl HAVE_CLOSESOCKET will be defined. AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - AC_REQUIRE([CURL_INCLUDES_SOCKET])dnl # tst_links_closesocket="unknown" tst_proto_closesocket="unknown" @@ -705,7 +659,6 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_LINK_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_winsock2 - $curl_includes_socket ]],[[ if(0 != closesocket(0)) return 1; @@ -722,7 +675,6 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_MSG_CHECKING([if closesocket is prototyped]) AC_EGREP_CPP([closesocket],[ $curl_includes_winsock2 - $curl_includes_socket ],[ AC_MSG_RESULT([yes]) tst_proto_closesocket="yes" @@ -737,7 +689,6 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOSESOCKET], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_winsock2 - $curl_includes_socket ]],[[ if(0 != closesocket(0)) return 1; @@ -1533,52 +1484,7 @@ AC_DEFUN([CURL_CHECK_FUNC_GETADDRINFO], [ tst_tsafe_getaddrinfo="yes" fi if test "$tst_tsafe_getaddrinfo" = "unknown"; then - CURL_CHECK_DEF_CC([h_errno], [ - $curl_includes_sys_socket - $curl_includes_netdb - ], [silent]) - if test "$curl_cv_have_def_h_errno" = "yes"; then - tst_h_errno_macro="yes" - else - tst_h_errno_macro="no" - fi - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_sys_socket - $curl_includes_netdb - ]],[[ - h_errno = 2; - if(0 != h_errno) - return 1; - ]]) - ],[ - tst_h_errno_modifiable_lvalue="yes" - ],[ - tst_h_errno_modifiable_lvalue="no" - ]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - ]],[[ - #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L) - return 0; - #elif defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 700) - return 0; - #else - #error force compilation error - #endif - ]]) - ],[ - tst_h_errno_sbs_issue_7="yes" - ],[ - tst_h_errno_sbs_issue_7="no" - ]) - if test "$tst_h_errno_macro" = "no" && - test "$tst_h_errno_modifiable_lvalue" = "no" && - test "$tst_h_errno_sbs_issue_7" = "no"; then - tst_tsafe_getaddrinfo="no" - else - tst_tsafe_getaddrinfo="yes" - fi + tst_tsafe_getaddrinfo="yes" fi AC_MSG_RESULT([$tst_tsafe_getaddrinfo]) if test "$tst_tsafe_getaddrinfo" = "yes"; then @@ -2158,101 +2064,6 @@ AC_DEFUN([CURL_CHECK_FUNC_GETSOCKNAME], [ fi ]) -dnl CURL_CHECK_FUNC_IF_NAMETOINDEX -dnl ------------------------------------------------- -dnl Verify if if_nametoindex 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_if_nametoindex, then -dnl HAVE_IF_NAMETOINDEX will be defined. - -AC_DEFUN([CURL_CHECK_FUNC_IF_NAMETOINDEX], [ - AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl - AC_REQUIRE([CURL_INCLUDES_NETIF])dnl - AC_REQUIRE([CURL_PREPROCESS_CALLCONV])dnl - # - tst_links_if_nametoindex="unknown" - tst_proto_if_nametoindex="unknown" - tst_compi_if_nametoindex="unknown" - tst_allow_if_nametoindex="unknown" - # - AC_MSG_CHECKING([if if_nametoindex can be linked]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_winsock2 - $curl_includes_bsdsocket - #include - ]],[[ - if(0 != if_nametoindex("")) - return 1; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_links_if_nametoindex="yes" - ],[ - AC_MSG_RESULT([no]) - tst_links_if_nametoindex="no" - ]) - # - if test "$tst_links_if_nametoindex" = "yes"; then - AC_MSG_CHECKING([if if_nametoindex is prototyped]) - AC_EGREP_CPP([if_nametoindex],[ - $curl_includes_winsock2 - $curl_includes_netif - ],[ - AC_MSG_RESULT([yes]) - tst_proto_if_nametoindex="yes" - ],[ - AC_MSG_RESULT([no]) - tst_proto_if_nametoindex="no" - ]) - fi - # - if test "$tst_proto_if_nametoindex" = "yes"; then - AC_MSG_CHECKING([if if_nametoindex is compilable]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_winsock2 - $curl_includes_netif - ]],[[ - if(0 != if_nametoindex("")) - return 1; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_compi_if_nametoindex="yes" - ],[ - AC_MSG_RESULT([no]) - tst_compi_if_nametoindex="no" - ]) - fi - # - if test "$tst_compi_if_nametoindex" = "yes"; then - AC_MSG_CHECKING([if if_nametoindex usage allowed]) - if test "x$curl_disallow_if_nametoindex" != "xyes"; then - AC_MSG_RESULT([yes]) - tst_allow_if_nametoindex="yes" - else - AC_MSG_RESULT([no]) - tst_allow_if_nametoindex="no" - fi - fi - # - AC_MSG_CHECKING([if if_nametoindex might be used]) - if test "$tst_links_if_nametoindex" = "yes" && - test "$tst_proto_if_nametoindex" = "yes" && - test "$tst_compi_if_nametoindex" = "yes" && - test "$tst_allow_if_nametoindex" = "yes"; then - AC_MSG_RESULT([yes]) - AC_DEFINE_UNQUOTED(HAVE_IF_NAMETOINDEX, 1, - [Define to 1 if you have the if_nametoindex function.]) - curl_cv_func_if_nametoindex="yes" - else - AC_MSG_RESULT([no]) - curl_cv_func_if_nametoindex="no" - fi -]) - dnl CURL_CHECK_FUNC_GETIFADDRS dnl ------------------------------------------------- @@ -3395,74 +3206,6 @@ AC_DEFUN([CURL_CHECK_FUNC_MEMRCHR], [ ]) -dnl CURL_CHECK_FUNC_POLL -dnl ------------------------------------------------- -dnl Verify if poll is available, prototyped, can -dnl be compiled and seems to work. - -AC_DEFUN([CURL_CHECK_FUNC_POLL], [ - AC_REQUIRE([CURL_INCLUDES_STDLIB])dnl - AC_REQUIRE([CURL_INCLUDES_POLL])dnl - # - tst_links_poll="unknown" - tst_proto_poll="unknown" - tst_compi_poll="unknown" - # - AC_MSG_CHECKING([if poll can be linked]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_poll - ]],[[ - if(0 != poll(0, 0, 0)) - return 1; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_links_poll="yes" - ],[ - AC_MSG_RESULT([no]) - tst_links_poll="no" - ]) - # - if test "$tst_links_poll" = "yes"; then - AC_MSG_CHECKING([if poll is prototyped]) - AC_EGREP_CPP([poll],[ - $curl_includes_poll - ],[ - AC_MSG_RESULT([yes]) - tst_proto_poll="yes" - ],[ - AC_MSG_RESULT([no]) - tst_proto_poll="no" - ]) - fi - # - if test "$tst_proto_poll" = "yes"; then - AC_MSG_CHECKING([if poll is compilable]) - AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - $curl_includes_poll - ]],[[ - if(0 != poll(0, 0, 0)) - return 1; - ]]) - ],[ - AC_MSG_RESULT([yes]) - tst_compi_poll="yes" - AC_DEFINE_UNQUOTED(HAVE_POLL, 1, [If you have poll]) - ],[ - AC_MSG_RESULT([no]) - tst_compi_poll="no" - ]) - fi - # -]) - - - fi -]) - - dnl CURL_CHECK_FUNC_SIGACTION dnl ------------------------------------------------- dnl Verify if sigaction is available, prototyped, and @@ -3836,7 +3579,6 @@ dnl HAVE_SOCKET will be defined. AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ AC_REQUIRE([CURL_INCLUDES_WINSOCK2])dnl AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET])dnl - AC_REQUIRE([CURL_INCLUDES_SOCKET])dnl # tst_links_socket="unknown" tst_proto_socket="unknown" @@ -3849,7 +3591,6 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_winsock2 $curl_includes_bsdsocket $curl_includes_sys_socket - $curl_includes_socket ]],[[ if(0 != socket(0, 0, 0)) return 1; @@ -3868,7 +3609,6 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_winsock2 $curl_includes_bsdsocket $curl_includes_sys_socket - $curl_includes_socket ],[ AC_MSG_RESULT([yes]) tst_proto_socket="yes" @@ -3885,7 +3625,6 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKET], [ $curl_includes_winsock2 $curl_includes_bsdsocket $curl_includes_sys_socket - $curl_includes_socket ]],[[ if(0 != socket(0, 0, 0)) return 1; @@ -3936,7 +3675,6 @@ dnl HAVE_SOCKETPAIR will be defined. AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ AC_REQUIRE([CURL_INCLUDES_SYS_SOCKET])dnl - AC_REQUIRE([CURL_INCLUDES_SOCKET])dnl # tst_links_socketpair="unknown" tst_proto_socketpair="unknown" @@ -3958,7 +3696,6 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ AC_MSG_CHECKING([if socketpair is prototyped]) AC_EGREP_CPP([socketpair],[ $curl_includes_sys_socket - $curl_includes_socket ],[ AC_MSG_RESULT([yes]) tst_proto_socketpair="yes" @@ -3973,7 +3710,6 @@ AC_DEFUN([CURL_CHECK_FUNC_SOCKETPAIR], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ $curl_includes_sys_socket - $curl_includes_socket ]],[[ int sv[2]; if(0 != socketpair(0, 0, 0, sv)) diff --git a/m4/curl-gnutls.m4 b/m4/curl-gnutls.m4 index 93c3946a93746c..8601dc2420effb 100644 --- a/m4/curl-gnutls.m4 +++ b/m4/curl-gnutls.m4 @@ -89,9 +89,11 @@ if test "x$OPT_GNUTLS" != xno; then CLEANLIBS="$LIBS" CLEANCPPFLAGS="$CPPFLAGS" CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" LIBS="$addlib $LIBS" LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" if test "$addcflags" != "-I/usr/include"; then CPPFLAGS="$CPPFLAGS $addcflags" fi diff --git a/m4/curl-mbedtls.m4 b/m4/curl-mbedtls.m4 index bf14c6c893bfd0..e0ea987e0c2726 100644 --- a/m4/curl-mbedtls.m4 +++ b/m4/curl-mbedtls.m4 @@ -30,6 +30,7 @@ AC_DEFUN([CURL_WITH_MBEDTLS], [ if test "x$OPT_MBEDTLS" != xno; then _cppflags=$CPPFLAGS _ldflags=$LDFLAGS + _ldflagspc=$LDFLAGSPC ssl_msg= if test X"$OPT_MBEDTLS" != Xno; then @@ -65,6 +66,7 @@ if test "x$OPT_MBEDTLS" != xno; then mbedtlslib=$OPT_MBEDTLS/lib$libsuff LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" if test "$addcflags" != "-I/usr/include"; then CPPFLAGS="$CPPFLAGS $addcflags" fi @@ -81,6 +83,7 @@ if test "x$OPT_MBEDTLS" != xno; then [ CPPFLAGS=$_cppflags LDFLAGS=$_ldflags + LDFLAGSPC=$_ldflagspc ], -lmbedx509 -lmbedcrypto) fi @@ -101,7 +104,10 @@ if test "x$OPT_MBEDTLS" != xno; then AC_MSG_NOTICE([Added $mbedtlslib to CURL_LIBRARY_PATH]) fi fi - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mbedtls" + dnl FIXME: Enable when mbedTLS was detected via pkg-config + if false; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE mbedtls" + fi fi fi dnl mbedTLS not disabled diff --git a/m4/curl-openssl.m4 b/m4/curl-openssl.m4 index dfbbc3394df3f2..a3ccd71cec6550 100644 --- a/m4/curl-openssl.m4 +++ b/m4/curl-openssl.m4 @@ -35,6 +35,7 @@ if test "x$OPT_OPENSSL" != xno; then dnl backup the pre-ssl variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" CLEANLIBS="$LIBS" @@ -139,6 +140,7 @@ if test "x$OPT_OPENSSL" != xno; then dnl finally, set flags to use SSL CPPFLAGS="$CPPFLAGS $SSL_CPPFLAGS" LDFLAGS="$LDFLAGS $SSL_LDFLAGS" + LDFLAGSPC="$LDFLAGSPC $SSL_LDFLAGS" AC_CHECK_LIB(crypto, HMAC_Update,[ HAVECRYPTO="yes" @@ -146,6 +148,7 @@ if test "x$OPT_OPENSSL" != xno; then ],[ if test -n "$LIB_OPENSSL" ; then LDFLAGS="$CLEANLDFLAGS -L$LIB_OPENSSL" + LDFLAGSPC="$CLEANLDFLAGSPC -L$LIB_OPENSSL" fi if test "$PKGCONFIG" = "no" -a -n "$PREFIX_OPENSSL" ; then # only set this if pkg-config wasn't used @@ -190,6 +193,7 @@ if test "x$OPT_OPENSSL" != xno; then [ AC_MSG_RESULT(no) LDFLAGS="$CLEANLDFLAGS" + LDFLAGSPC="$CLEANLDFLAGSPC" CPPFLAGS="$CLEANCPPFLAGS" LIBS="$CLEANLIBS" ]) diff --git a/m4/curl-rustls.m4 b/m4/curl-rustls.m4 index fa0118ec7f31a4..f91f0f423c6d91 100644 --- a/m4/curl-rustls.m4 +++ b/m4/curl-rustls.m4 @@ -32,15 +32,9 @@ if test "x$OPT_RUSTLS" != xno; then dnl backup the pre-ssl variables CLEANLDFLAGS="$LDFLAGS" + CLEANLDFLAGSPC="$LDFLAGSPC" CLEANCPPFLAGS="$CPPFLAGS" - case $host in - *-apple-*) - LDFLAGS="$LDFLAGS -framework Security" - ;; - *) - ;; - esac ## NEW CODE dnl use pkg-config unless we have been given a path @@ -84,10 +78,19 @@ if test "x$OPT_RUSTLS" != xno; then addcflags=-I$PREFIX_RUSTLS/include LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" if test "$addcflags" != "-I/usr/include"; then CPPFLAGS="$CPPFLAGS $addcflags" fi + case $host in + *-apple-*) + RUSTLS_LDFLAGS="-framework Security -framework Foundation" + ;; + *) + RUSTLS_LDFLAGS="-lpthread -ldl -lm" + ;; + esac AC_CHECK_LIB(rustls, rustls_connection_read, [ AC_DEFINE(USE_RUSTLS, 1, [if Rustls is enabled]) @@ -98,17 +101,19 @@ if test "x$OPT_RUSTLS" != xno; then test rustls != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes ], AC_MSG_ERROR([--with-rustls was specified but could not find Rustls.]), - -lpthread -ldl -lm) + $RUSTLS_LDFLAGS) LIB_RUSTLS="$PREFIX_RUSTLS/lib$libsuff" if test "$PREFIX_RUSTLS" != "/usr" ; then - SSL_LDFLAGS="-L$LIB_RUSTLS" + SSL_LDFLAGS="-L$LIB_RUSTLS $RUSTLS_LDFLAGS" SSL_CPPFLAGS="-I$PREFIX_RUSTLS/include" fi fi ;; esac + link_pkgconfig='' + if test "$PKGTEST" = "yes"; then CURL_CHECK_PKGCONFIG(rustls, [$RUSTLS_PCDIR]) @@ -137,6 +142,7 @@ if test "x$OPT_RUSTLS" != xno; then dnl additional libs may be necessary. Hope that we dnl don't need any. LIBS="$SSL_LIBS $LIBS" + link_pkgconfig=1 ssl_msg="rustls" AC_DEFINE(USE_RUSTLS, 1, [if Rustls is enabled]) AC_SUBST(USE_RUSTLS, [1]) @@ -154,8 +160,9 @@ if test "x$OPT_RUSTLS" != xno; then fi dnl finally, set flags to use this TLS backend - CPPFLAGS="$CLEAN_CPPFLAGS $SSL_CPPFLAGS" - LDFLAGS="$CLAN_LDFLAGS $SSL_LDFLAGS" + CPPFLAGS="$CLEANCPPFLAGS $SSL_CPPFLAGS" + LDFLAGS="$CLEANLDFLAGS $SSL_LDFLAGS" + LDFLAGSPC="$CLEANLDFLAGSPC $SSL_LDFLAGS" if test "x$USE_RUSTLS" = "xyes"; then AC_MSG_NOTICE([detected Rustls]) @@ -172,7 +179,9 @@ if test "x$OPT_RUSTLS" != xno; then AC_MSG_NOTICE([Added $LIB_RUSTLS to CURL_LIBRARY_PATH]) fi fi - LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE rustls" + if test -n "$link_pkgconfig"; then + LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE rustls" + fi fi test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" diff --git a/m4/curl-sectransp.m4 b/m4/curl-sectransp.m4 index 77b37bed9d7233..234fb13b922e2a 100644 --- a/m4/curl-sectransp.m4 +++ b/m4/curl-sectransp.m4 @@ -33,7 +33,9 @@ if test "x$OPT_SECURETRANSPORT" != xno; then ssl_msg="Secure Transport" test secure-transport != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes SECURETRANSPORT_ENABLED=1 - LDFLAGS="$LDFLAGS -framework CoreFoundation -framework CoreServices -framework Security" + SECURETRANSPORT_LDFLAGS='-framework CoreFoundation -framework CoreServices -framework Security' + LDFLAGS="$LDFLAGS $SECURETRANSPORT_LDFLAGS" + LDFLAGSPC="$LDFLAGSPC $SECURETRANSPORT_LDFLAGS" else AC_MSG_RESULT(no) fi diff --git a/m4/curl-sysconfig.m4 b/m4/curl-sysconfig.m4 index 5fcd8859d37ad2..a0ddc0ba5556ec 100644 --- a/m4/curl-sysconfig.m4 +++ b/m4/curl-sysconfig.m4 @@ -44,7 +44,9 @@ case $host in ]) if test "x$build_for_macos" != xno; then AC_MSG_RESULT(yes) - LDFLAGS="$LDFLAGS -framework CoreFoundation -framework CoreServices -framework SystemConfiguration" + SYSCONFIG_LDFLAGS='-framework CoreFoundation -framework CoreServices -framework SystemConfiguration' + LDFLAGS="$LDFLAGS $SYSCONFIG_LDFLAGS" + LDFLAGSPC="$LDFLAGSPC $SYSCONFIG_LDFLAGS" else AC_MSG_RESULT(no) fi diff --git a/m4/curl-wolfssl.m4 b/m4/curl-wolfssl.m4 index ca60d3b48815a2..ec8e3d5f614ce3 100644 --- a/m4/curl-wolfssl.m4 +++ b/m4/curl-wolfssl.m4 @@ -39,6 +39,7 @@ esac if test "x$OPT_WOLFSSL" != xno; then _cppflags=$CPPFLAGS _ldflags=$LDFLAGS + _ldflagspc=$LDFLAGSPC ssl_msg= @@ -78,6 +79,7 @@ if test "x$OPT_WOLFSSL" != xno; then if test "x$USE_WOLFSSL" != "xyes"; then LDFLAGS="$LDFLAGS $addld" + LDFLAGSPC="$LDFLAGSPC $addld" AC_MSG_NOTICE([Add $addld to LDFLAGS]) if test "$addcflags" != "-I/usr/include"; then CPPFLAGS="$CPPFLAGS $addcflags" @@ -114,6 +116,7 @@ if test "x$OPT_WOLFSSL" != xno; then AC_MSG_RESULT(no) CPPFLAGS=$_cppflags LDFLAGS=$_ldflags + LDFLAGSPC=$_ldflagspc wolfssllibpath="" ]) LIBS="$my_ac_save_LIBS" @@ -145,6 +148,14 @@ if test "x$OPT_WOLFSSL" != xno; then ) dnl if this symbol is present, we can make use of BIO filter chains + AC_CHECK_FUNC(wolfSSL_BIO_new, + [ + AC_DEFINE(HAVE_WOLFSSL_BIO, 1, + [if you have wolfSSL_BIO_new]) + WOLFSSL_BIO=1 + ] + ) + dnl if this symbol is present, we have the full BIO feature set AC_CHECK_FUNC(wolfSSL_BIO_set_shutdown, [ AC_DEFINE(HAVE_WOLFSSL_FULL_BIO, 1, diff --git a/packages/OS400/ccsidcurl.c b/packages/OS400/ccsidcurl.c index fe2b4589c542a6..48f7f6d4aeb129 100644 --- a/packages/OS400/ccsidcurl.c +++ b/packages/OS400/ccsidcurl.c @@ -1072,6 +1072,7 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) char *cp = NULL; unsigned int ccsid; curl_off_t pfsize; + struct Curl_easy *data = easy; va_start(arg, tag); @@ -1195,7 +1196,7 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) s = va_arg(arg, char *); ccsid = va_arg(arg, unsigned int); - pfsize = easy->set.postfieldsize; + pfsize = data->set.postfieldsize; if(!s || !pfsize || ccsid == NOCONV_CCSID || ccsid == ASCII_CCSID) { result = curl_easy_setopt(easy, CURLOPT_COPYPOSTFIELDS, s); @@ -1240,12 +1241,12 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) break; } - easy->set.postfieldsize = pfsize; /* Replace data size. */ + data->set.postfieldsize = pfsize; /* Replace data size. */ s = cp; } result = curl_easy_setopt(easy, CURLOPT_POSTFIELDS, s); - easy->set.str[STRING_COPYPOSTFIELDS] = s; /* Give to library. */ + data->set.str[STRING_COPYPOSTFIELDS] = s; /* Give to library. */ break; default: diff --git a/packages/OS400/curlmain.c b/packages/OS400/curlmain.c index 1b030b7ca324fc..079b9b0d9dcda2 100644 --- a/packages/OS400/curlmain.c +++ b/packages/OS400/curlmain.c @@ -61,15 +61,15 @@ int main(int argc, char **argv) size_t inbytesleft; size_t outbytesleft; char dummybuf[128]; - char tocode[32]; - char fromcode[32]; + /* To/From codes are 32 byte long strings with + reserved fields initialized to ZEROs */ + const char tocode[32] = {"IBMCCSID01208"}; /* Use UTF-8. */ + const char fromcode[32] = {"IBMCCSID000000000010"}; ebcdic_argc = argc; ebcdic_argv = argv; /* Build the encoding converter. */ - strncpy(tocode, "IBMCCSID01208", sizeof(tocode)); /* Use UTF-8. */ - strncpy(fromcode, "IBMCCSID000000000010", sizeof(fromcode)); cd = iconv_open(tocode, fromcode); /* Measure the arguments. */ diff --git a/packages/OS400/initscript.sh b/packages/OS400/initscript.sh index 2f6d78c24503ae..69a354ab0d0c14 100755 --- a/packages/OS400/initscript.sh +++ b/packages/OS400/initscript.sh @@ -189,8 +189,8 @@ make_module() echo "#pragma convert(819)" echo "#line 1" cat "${2}" - } > __tmpsrcf.c - CMD="CRTCMOD MODULE(${TARGETLIB}/${1}) SRCSTMF('__tmpsrcf.c')" + } > "${1}"__819.c + CMD="CRTCMOD MODULE(${TARGETLIB}/${1}) SRCSTMF('${1}__819.c')" CMD="${CMD} SYSIFCOPT(*IFS64IO *ASYNCSIGNAL)" # CMD="${CMD} OPTION(*INCDIRFIRST *SHOWINC *SHOWSYS)" CMD="${CMD} OPTION(*INCDIRFIRST)" @@ -228,7 +228,9 @@ make_module() fi CLcommand "${CMD}" - rm -f __tmpsrcf.c + if [ "${DEBUG}" = "*NONE" ] + then rm -f "${1}"__819.c + fi # shellcheck disable=SC2034 LINK=YES } diff --git a/packages/vms/generate_config_vms_h_curl.com b/packages/vms/generate_config_vms_h_curl.com index 623cd5fe73dc9d..81ede597ea79b1 100644 --- a/packages/vms/generate_config_vms_h_curl.com +++ b/packages/vms/generate_config_vms_h_curl.com @@ -203,13 +203,13 @@ $! Now the DCL builds usually say xxx-HP-VMS and configure scripts $! may put DEC or COMPAQ or HP for the middle part. $! $write cvh "#if defined(__alpha)" -$write cvh "#define OS ""ALPHA-HP-VMS""" +$write cvh "#define CURL_OS ""ALPHA-HP-VMS""" $write cvh "#elif defined(__vax)" -$write cvh "#define OS ""VAX-HP-VMS""" +$write cvh "#define CURL_OS ""VAX-HP-VMS""" $write cvh "#elif defined(__ia64)" -$write cvh "#define OS ""IA64-HP-VMS"" +$write cvh "#define CURL_OS ""IA64-HP-VMS"" $write cvh "#else" -$write cvh "#define OS ""UNKNOWN-HP-VMS"" +$write cvh "#define CURL_OS ""UNKNOWN-HP-VMS"" $write cvh "#endif" $write cvh "" $! diff --git a/renovate.json b/renovate.json index 2a4b0c676c78e5..f8b540785c33a3 100644 --- a/renovate.json +++ b/renovate.json @@ -75,13 +75,26 @@ "^.github/workflows/http3-linux.yml$" ], "matchStrings": [ - "openssl3-version: (?.*)\\s" + "openssl-version: (?.*)\\s" ], "datasourceTemplate": "github-tags", "depNameTemplate": "openssl/openssl", "versioningTemplate": "semver", - "extractVersionTemplate": "^openssl-(?\\d+)\\.(?\\d+)\\.(?\\d+)$", - "registryUrlTemplate": "https://github.com" + "extractVersionTemplate": "^openssl-(?.*)$" + }, + { + "customType": "regex", + "fileMatch": [ + "^.github/workflows/linux.yml$", + "^.github/workflows/http3-linux.yml$" + ], + "matchStrings": [ + "quictls-version: (?.*)\\s" + ], + "datasourceTemplate": "github-tags", + "depNameTemplate": "quictls/openssl", + "versioningTemplate": "semver", + "extractVersionTemplate": "^openssl-(?.*)-quic1$" } ] } diff --git a/scripts/checksrc.pl b/scripts/checksrc.pl index 0f727e38b988ef..7075278de2f7cc 100755 --- a/scripts/checksrc.pl +++ b/scripts/checksrc.pl @@ -80,6 +80,7 @@ 'LONGLINE' => "Line longer than $max_column", 'SPACEBEFORELABEL' => 'labels not at the start of the line', 'MULTISPACE' => 'multiple spaces used when not suitable', + 'NOSPACEAND' => 'missing space around Logical AND operator', 'NOSPACEC' => 'missing space around ternary colon operator', 'NOSPACEEQUALS' => 'equals sign without preceding space', 'NOSPACEQ' => 'missing space around ternary question mark operator', @@ -626,6 +627,20 @@ sub scanfile { "space after open parenthesis"); } + # check spaces before Logical AND operator + if($nostr =~ /^(.*)\w&&/i) { + checkwarn("NOSPACEAND", + $line, length($1)+1, $file, $l, + "missing space before Logical AND"); + } + + # check spaces after Logical AND operator + if($nostr =~ /^(.*&&)\w/i) { + checkwarn("NOSPACEAND", + $line, length($1), $file, $l, + "missing space after Logical AND"); + } + # check spaces before colon if($nostr =~ /^(.*[^']\?[^'].*)(\w|\)|\]|')\:/i) { my $m = $1; diff --git a/scripts/cmakelint.sh b/scripts/cmakelint.sh index 4eb54e145d0eee..070079656972b1 100755 --- a/scripts/cmakelint.sh +++ b/scripts/cmakelint.sh @@ -43,7 +43,7 @@ # strip off the leading ./ to make the grep regexes work properly find . -type f | sed 's@^\./@@' fi -} | grep -E '(/CMake|\.cmake$)' | grep -v -E '(\.h\.cmake|\.in)$' \ +} | grep -E '(^CMake|/CMake|\.cmake$)' | grep -v -E '(\.h\.cmake|\.in)$' \ | xargs \ cmakelint \ --spaces=2 --linelength=132 \ diff --git a/scripts/mk-ca-bundle.pl b/scripts/mk-ca-bundle.pl index 07eabbe8552a1e..8e8afb4dd74b9a 100755 --- a/scripts/mk-ca-bundle.pl +++ b/scripts/mk-ca-bundle.pl @@ -553,48 +553,6 @@ (%) } next; } - elsif (/^CKA_NSS_SERVER_DISTRUST_AFTER (CK_BBOOL CK_FALSE|MULTILINE_OCTAL)/) { - # Example: - # CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL - # \062\060\060\066\061\067\060\060\060\060\060\060\132 - # END - if($1 eq "MULTILINE_OCTAL") { - my @timestamp; - while () { - last if (/^END/); - chomp; - my @octets = split(/\\/); - shift @octets; - for (@octets) { - push @timestamp, chr(oct); - } - } - scalar(@timestamp) == 13 or die "Failed parsing timestamp"; - # A trailing Z in the timestamp signifies UTC - if($timestamp[12] ne "Z") { - report "distrust date stamp is not using UTC"; - } - # Example date: 200617000000Z - # Means 2020-06-17 00:00:00 UTC - my $distrustat = - timegm($timestamp[10] . $timestamp[11], # second - $timestamp[8] . $timestamp[9], # minute - $timestamp[6] . $timestamp[7], # hour - $timestamp[4] . $timestamp[5], # day - ($timestamp[2] . $timestamp[3]) - 1, # month - "20" . $timestamp[0] . $timestamp[1]); # year - if(time >= $distrustat) { - # not trusted anymore - $skipnum++; - report "Skipping: $main_block_name is not trusted anymore" if ($opt_v); - $valid = 0; - } - else { - # still trusted - } - } - next; - } else { next; } diff --git a/src/.checksrc b/src/.checksrc index 946367c4999f6f..df9b1f0795a728 100644 --- a/src/.checksrc +++ b/src/.checksrc @@ -1 +1,2 @@ enable STDERR +enable STRNCPY diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1458826e39beb3..0f87fcc98fbd7e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,10 +118,10 @@ source_group("curl source files" FILES ${CURL_CFILES}) source_group("curl header files" FILES ${CURL_HFILES}) include_directories( - "${CURL_BINARY_DIR}/lib" # for "curl_config.h" - "${CURL_SOURCE_DIR}/lib" # for "curl_setup.h" + "${PROJECT_BINARY_DIR}/lib" # for "curl_config.h" + "${PROJECT_SOURCE_DIR}/lib" # for "curl_setup.h" # This is needed as tool_hugehelp.c is generated in the binary dir - "${CURL_SOURCE_DIR}/src" # for "tool_hugehelp.h" + "${PROJECT_SOURCE_DIR}/src" # for "tool_hugehelp.h" ) # Build curl executable diff --git a/src/mk-file-embed.pl b/src/mk-file-embed.pl index 3447aa9472b717..e4dbe35de1acdc 100755 --- a/src/mk-file-embed.pl +++ b/src/mk-file-embed.pl @@ -29,11 +29,16 @@ $varname = shift @ARGV; } +my $varname_upper = uc($varname); + print < @@ -42,6 +42,8 @@ void set_binmode(FILE *stream) #ifdef O_BINARY # ifdef __HIGHC__ _setmode(stream, O_BINARY); +# elif defined(HAVE__SETMODE) + (void)_setmode(fileno(stream), O_BINARY); # else (void)setmode(fileno(stream), O_BINARY); # endif @@ -50,4 +52,4 @@ void set_binmode(FILE *stream) #endif } -#endif /* HAVE_SETMODE */ +#endif /* HAVE_SETMODE || HAVE__SETMODE */ diff --git a/src/tool_binmode.h b/src/tool_binmode.h index bee837b0001b29..ce2f589e0ffcb1 100644 --- a/src/tool_binmode.h +++ b/src/tool_binmode.h @@ -25,7 +25,7 @@ ***************************************************************************/ #include "tool_setup.h" -#ifdef HAVE_SETMODE +#if defined(HAVE_SETMODE) || defined(HAVE__SETMODE) void set_binmode(FILE *stream); @@ -33,6 +33,6 @@ void set_binmode(FILE *stream); #define set_binmode(x) Curl_nop_stmt -#endif /* HAVE_SETMODE */ +#endif /* HAVE_SETMODE || HAVE__SETMODE */ #endif /* HEADER_CURL_TOOL_BINMODE_H */ diff --git a/src/tool_cb_dbg.c b/src/tool_cb_dbg.c index 6d2a6178358336..195127bd8bad05 100644 --- a/src/tool_cb_dbg.c +++ b/src/tool_cb_dbg.c @@ -173,7 +173,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, log_line_start(output, timebuf, idsbuf, type); (void)fwrite(data + st, i - st + 1, 1, output); } - newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE; + newl = (size && (data[size - 1] != '\n')); traced_data = FALSE; break; case CURLINFO_TEXT: @@ -181,7 +181,7 @@ int tool_debug_cb(CURL *handle, curl_infotype type, if(!newl) log_line_start(output, timebuf, idsbuf, type); (void)fwrite(data, size, 1, output); - newl = (size && (data[size - 1] != '\n')) ? TRUE : FALSE; + newl = (size && (data[size - 1] != '\n')); traced_data = FALSE; break; case CURLINFO_DATA_OUT: diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c index 48808adb2701bc..d7ee7b1b224a9b 100644 --- a/src/tool_cfgable.c +++ b/src/tool_cfgable.c @@ -173,21 +173,14 @@ static void free_config_fields(struct OperationConfig *config) Curl_safefree(config->preproxy); Curl_safefree(config->proxy_service_name); Curl_safefree(config->service_name); - Curl_safefree(config->ftp_account); Curl_safefree(config->ftp_alternative_to_user); - Curl_safefree(config->aws_sigv4); Curl_safefree(config->proto_str); Curl_safefree(config->proto_redir_str); -#ifdef USE_ECH Curl_safefree(config->ech); - config->ech = NULL; Curl_safefree(config->ech_config); - config->ech_config = NULL; Curl_safefree(config->ech_public); - config->ech_public = NULL; -#endif } void config_free(struct OperationConfig *config) diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index d9099c329ddb25..ae59a9bf672dc2 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -261,6 +261,7 @@ struct OperationConfig { bool xattr; /* store metadata in extended attributes */ long gssapi_delegation; bool ssl_allow_beast; /* allow this SSL vulnerability */ + bool ssl_allow_earlydata; /* allow use of TLSv1.3 early data */ bool proxy_ssl_allow_beast; /* allow this SSL vulnerability for proxy */ bool ssl_no_revoke; /* disable SSL certificate revocation checks */ bool ssl_revoke_best_effort; /* ignore SSL revocation offline/missing @@ -305,12 +306,9 @@ struct OperationConfig { bool rm_partial; /* on error, remove partially written output files */ bool skip_existing; -#ifdef USE_ECH char *ech; /* Config set by --ech keywords */ char *ech_config; /* Config set by "--ech esl:" option */ char *ech_public; /* Config set by "--ech pn:" option */ -#endif - }; struct GlobalConfig { @@ -332,13 +330,14 @@ struct GlobalConfig { long ms_per_transfer; /* start next transfer after (at least) this many milliseconds */ #ifdef DEBUGBUILD + bool test_duphandle; bool test_event_based; #endif bool parallel; unsigned short parallel_max; /* MAX_PARALLEL is the maximum */ bool parallel_connect; char *help_category; /* The help category, if set */ - struct var *variables; + struct tool_var *variables; struct OperationConfig *first; struct OperationConfig *current; struct OperationConfig *last; /* Always last in the struct */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index 9a5de1e731e477..447046f72f866e 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -312,10 +312,14 @@ static const struct LongShort aliases[]= { {"tcp-fastopen", ARG_BOOL, ' ', C_TCP_FASTOPEN}, {"tcp-nodelay", ARG_BOOL, ' ', C_TCP_NODELAY}, {"telnet-option", ARG_STRG, 't', C_TELNET_OPTION}, +#ifdef DEBUGBUILD + {"test-duphandle", ARG_BOOL, ' ', C_TEST_DUPHANDLE}, {"test-event", ARG_BOOL, ' ', C_TEST_EVENT}, +#endif {"tftp-blksize", ARG_STRG, ' ', C_TFTP_BLKSIZE}, {"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS}, {"time-cond", ARG_STRG, 'z', C_TIME_COND}, + {"tls-earlydata", ARG_BOOL, ' ', C_TLS_EARLYDATA}, {"tls-max", ARG_STRG, ' ', C_TLS_MAX}, {"tls13-ciphers", ARG_STRG, ' ', C_TLS13_CIPHERS}, {"tlsauthtype", ARG_STRG, ' ', C_TLSAUTHTYPE}, @@ -390,7 +394,7 @@ void parse_cert_parameter(const char *cert_parameter, param_place = cert_parameter; while(*param_place) { span = strcspn(param_place, ":\\"); - strncpy(certname_place, param_place, span); + memcpy(certname_place, param_place, span); param_place += span; certname_place += span; /* we just ate all the non-special chars. now we are on either a special @@ -944,7 +948,7 @@ static ParameterError set_rate(struct GlobalConfig *global, if(numlen > sizeof(number) -1) return PARAM_NUMBER_TOO_LARGE; - strncpy(number, nextarg, numlen); + memcpy(number, nextarg, numlen); number[numlen] = 0; err = str2unum(&denominator, number); if(err) @@ -1026,7 +1030,6 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ { int rc; const char *parse = NULL; - time_t now; bool longopt = FALSE; bool singleopt = FALSE; /* when true means '-o foo' used '-ofoo' */ size_t nopts = 0; /* options processed in `flag`*/ @@ -1218,7 +1221,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ config->disallow_username_in_url = toggle; break; case C_EPSV: /* --epsv */ - config->disable_epsv = (!toggle) ? TRUE : FALSE; + config->disable_epsv = !toggle; break; case C_DNS_SERVERS: /* --dns-servers */ if(!curlinfo->ares_num) /* c-ares is needed for this */ @@ -1248,7 +1251,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ } break; case C_ALPN: /* --alpn */ - config->noalpn = (!toggle) ? TRUE : FALSE; + config->noalpn = !toggle; break; case C_LIMIT_RATE: /* --limit-rate */ err = GetSizeParameter(global, nextarg, "rate", &value); @@ -1371,7 +1374,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ config->disable_eprt = toggle; break; case C_EPRT: /* --eprt */ - config->disable_eprt = (!toggle) ? TRUE : FALSE; + config->disable_eprt = !toggle; break; case C_XATTR: /* --xattr */ config->xattr = toggle; @@ -1552,7 +1555,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ config->ftp_ssl_reqd = toggle; break; case C_SESSIONID: /* --sessionid */ - config->disable_sessionid = (!toggle) ? TRUE : FALSE; + config->disable_sessionid = !toggle; break; case C_FTP_SSL_CONTROL: /* --ftp-ssl-control */ if(toggle && !feature_ssl) @@ -1582,7 +1585,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ config->raw = toggle; break; case C_KEEPALIVE: /* --keepalive */ - config->nokeepalive = (!toggle) ? TRUE : FALSE; + config->nokeepalive = !toggle; break; case C_KEEPALIVE_TIME: /* --keepalive-time */ err = str2unum(&config->alivetime, nextarg); @@ -1653,13 +1656,14 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case C_SASL_IR: /* --sasl-ir */ config->sasl_ir = toggle; break; - case C_TEST_EVENT: /* --test-event */ #ifdef DEBUGBUILD + case C_TEST_DUPHANDLE: /* --test-duphandle */ + global->test_duphandle = toggle; + break; + case C_TEST_EVENT: /* --test-event */ global->test_event_based = toggle; -#else - warnf(global, "--test-event is ignored unless a debug build"); -#endif break; +#endif case C_UNIX_SOCKET: /* --unix-socket */ config->abstract_unix_socket = FALSE; err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); @@ -1691,6 +1695,10 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ config->abstract_unix_socket = TRUE; err = getstr(&config->unix_socket_path, nextarg, DENY_BLANK); break; + case C_TLS_EARLYDATA: /* --tls-earlydata */ + if(feature_ssl) + config->ssl_allow_earlydata = toggle; + break; case C_TLS_MAX: /* --tls-max */ err = str2tls_max(&config->ssl_version_max, nextarg); break; @@ -1912,13 +1920,10 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ err = PARAM_ENGINES_REQUESTED; } break; -#ifndef USE_ECH - case C_ECH: /* --ech, not implemented by default */ - err = PARAM_LIBCURL_DOESNT_SUPPORT; - break; -#else case C_ECH: /* --ech */ - if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) { + if(!feature_ech) + err = PARAM_LIBCURL_DOESNT_SUPPORT; + else if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) { /* a public_name */ err = getstr(&config->ech_public, nextarg, DENY_BLANK); } @@ -1962,7 +1967,6 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ err = getstr(&config->ech, nextarg, DENY_BLANK); } break; -#endif case C_CAPATH: /* --capath */ err = getstr(&config->capath, nextarg, DENY_BLANK); break; @@ -2167,7 +2171,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ nextarg, &config->mimeroot, &config->mimecurrent, - (cmd == C_FORM_STRING) ? TRUE : FALSE)) /* literal string */ + (cmd == C_FORM_STRING))) /* literal string */ err = PARAM_BAD_USE; else if(SetHTTPrequest(config, TOOL_HTTPREQ_MIMEPOST, &config->httpreq)) err = PARAM_BAD_USE; @@ -2646,8 +2650,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ nextarg++; break; } - now = time(NULL); - config->condtime = (curl_off_t)curl_getdate(nextarg, &now); + config->condtime = (curl_off_t)curl_getdate(nextarg, NULL); if(-1 == config->condtime) { /* now let's see if it is a filename to get the time from instead! */ rc = getfiletime(nextarg, global, &value); diff --git a/src/tool_getparam.h b/src/tool_getparam.h index b22e60b7b34d95..7d9abbd1417cd2 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -267,10 +267,12 @@ typedef enum { C_TCP_FASTOPEN, C_TCP_NODELAY, C_TELNET_OPTION, + C_TEST_DUPHANDLE, C_TEST_EVENT, C_TFTP_BLKSIZE, C_TFTP_NO_OPTIONS, C_TIME_COND, + C_TLS_EARLYDATA, C_TLS_MAX, C_TLS13_CIPHERS, C_TLSAUTHTYPE, diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c index 34b79f5eb76faf..f6053e84003ee9 100644 --- a/src/tool_libinfo.c +++ b/src/tool_libinfo.c @@ -83,6 +83,7 @@ bool feature_spnego = FALSE; bool feature_ssl = FALSE; bool feature_tls_srp = FALSE; bool feature_zstd = FALSE; +bool feature_ech = FALSE; static struct feature_name_presentp { const char *feature_name; @@ -95,6 +96,7 @@ static struct feature_name_presentp { {"brotli", &feature_brotli, CURL_VERSION_BROTLI}, {"CharConv", NULL, CURL_VERSION_CONV}, {"Debug", NULL, CURL_VERSION_DEBUG}, + {"ECH", &feature_ech, 0}, {"gsasl", NULL, CURL_VERSION_GSASL}, {"GSS-API", NULL, CURL_VERSION_GSSAPI}, {"HSTS", &feature_hsts, CURL_VERSION_HSTS}, diff --git a/src/tool_libinfo.h b/src/tool_libinfo.h index ad9c195dc05666..0d176699e81d46 100644 --- a/src/tool_libinfo.h +++ b/src/tool_libinfo.h @@ -61,6 +61,7 @@ extern bool feature_spnego; extern bool feature_ssl; extern bool feature_tls_srp; extern bool feature_zstd; +extern bool feature_ech; CURLcode get_libcurl_info(void); const char *proto_token(const char *proto); diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index fa29a51c1f28bb..2d5f2b3ab97c0f 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -748,6 +748,9 @@ const struct helptxt helptext[] = { {"-z, --time-cond