Merge topic 'presets-file-arg' 95edd93948 presets: Support loading from arbitrary file 81c22a67b6 presets: Factor out command line arguments e75b62fd73 presets: Convert allowNoFiles to enum class 8476469b72 Help: Add cmake-workflow-option role 5e76987626 Help/presets: Use preset role for configurePreset references 2d8e8213d4 Help/presets: Improve include field formatting 43f5fc8ac8 Help, Tests: Consolidate presets terminology and references c49ebb67da Help: Use POSIX slashes for 'generated by' in presets files Acked-by: Kitware Robot <kwrobot@kitware.com> Tested-by: buildbot <buildbot@kitware.com> Merge-request: !11886
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fd56c98..7345496 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml
@@ -138,6 +138,18 @@ - .cmake_cdash_artifacts - .rules +l:pvs-studio-fedora43: + extends: + - .fedora43_pvs_studio + - .cmake_build_linux + - .linux_x86_64_tags + - .cmake_cdash_artifacts + - .rules + variables: + CMAKE_CI_JOB_CONTINUOUS: "true" + CMAKE_CI_JOB_NIGHTLY: "true" + CMAKE_CI_JOB_NO_MR: "true" + l:sphinx-fedora43: extends: - .fedora43_sphinx
diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore index 4d33c62..c0e73eb 100644 --- a/.gitlab/.gitignore +++ b/.gitlab/.gitignore
@@ -20,6 +20,7 @@ /open-watcom* /orangec /pellesc +/pvs-studio /python* /qt* /sccache*
diff --git a/.gitlab/ci/cmake.ps1 b/.gitlab/ci/cmake.ps1 index 94f6694..ee0f4db 100644 --- a/.gitlab/ci/cmake.ps1 +++ b/.gitlab/ci/cmake.ps1
@@ -1,12 +1,12 @@ $erroractionpreference = "stop" -$version = "4.2.0" +$version = "4.3.2" if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") { - $sha256sum = "CF35A516C4F5F4646B301E51C8E24B168CC012C3B1453B8F675303B54EB0EF45" + $sha256sum = "83D20C23F5C5F64B3B328785E35B23C532E33057A97ED6294ACACA3781B78A01" $platform = "windows-x86_64" } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") { - $sha256sum = "9EB3E88083DDA569A4086C48517F5D49BD7B505DADA79075EA7CB6E2AC6E0A1E" + $sha256sum = "42A2A88FC57E70D04A92F6EAAAC2094241995F53584BC2AFDAE7E6EF982B3781" $platform = "windows-arm64" } else { throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE")
diff --git a/.gitlab/ci/cmake.sh b/.gitlab/ci/cmake.sh index a0e2a5a..bc41ddd 100755 --- a/.gitlab/ci/cmake.sh +++ b/.gitlab/ci/cmake.sh
@@ -2,22 +2,22 @@ set -e -readonly version="4.2.0" +readonly version="4.3.2" case "$(uname -s)-$(uname -m)" in Linux-x86_64) shatool="sha256sum" - sha256sum="bbcebd4c433eab3af03a8c80bb5d84e8dfc3ff8a4ab9d01547b21240c23f7c2c" + sha256sum="791ae3604841ca03cb3889a3ad89165346e4b180ae3448efd4b0caa9ef46d245" platform="linux-x86_64" ;; Linux-aarch64) shatool="sha256sum" - sha256sum="86f52e9769cd7745e5227931e689e122dc1afc5648cc9b04db601b165f3ac993" + sha256sum="377079ab739f5765176f427609d9a2015b756ea20d5cba908d279c3731a2f481" platform="linux-aarch64" ;; Darwin-*) shatool="shasum -a 256" - sha256sum="b8b040a06343b2b6bc090b03a9c2bb4e98037518846989fb7c40ebbf30655c5d" + sha256sum="808ab43a0db04c8eec9ed7db12b90d7be1c8e2e75f4a060724d604a2043ccaf7" platform="macos-universal" ;; *)
diff --git a/.gitlab/ci/configure_fedora43_pvs_studio.cmake b/.gitlab/ci/configure_fedora43_pvs_studio.cmake new file mode 100644 index 0000000..ff6e7f0 --- /dev/null +++ b/.gitlab/ci/configure_fedora43_pvs_studio.cmake
@@ -0,0 +1,3 @@ +set(configure_no_sccache 1) +set(CMake_RUN_PVS_STUDIO ON CACHE BOOL "") +include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora43_common.cmake")
diff --git a/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh b/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh index 1ad42c4..3e76005 100755 --- a/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh +++ b/.gitlab/ci/docker/debian13-x86_64/install_rvm.sh
@@ -8,7 +8,10 @@ --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \ 7D2BAF1CF37B13E2069D6956105BD0E739499BDB -curl -sSL https://get.rvm.io | bash -s stable +curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer -o rvm-installer && +curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer.asc -o rvm-installer.asc && +gpg2 --verify rvm-installer.asc rvm-installer && +bash rvm-installer stable # keep version in sync with `env_debian*_ninja.sh` /usr/local/rvm/bin/rvm install ruby-3.2.2
diff --git a/.gitlab/ci/docker/fedora43/build_rvm.sh b/.gitlab/ci/docker/fedora43/build_rvm.sh index 22c4333..9da34fe 100755 --- a/.gitlab/ci/docker/fedora43/build_rvm.sh +++ b/.gitlab/ci/docker/fedora43/build_rvm.sh
@@ -8,7 +8,10 @@ --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \ 7D2BAF1CF37B13E2069D6956105BD0E739499BDB -curl -sSL https://get.rvm.io | bash -s stable --ignore-dotfiles +curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer -o rvm-installer && +curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer.asc -o rvm-installer.asc && +gpg2 --verify rvm-installer.asc rvm-installer && +bash rvm-installer stable --ignore-dotfiles export rvm_silence_banner=1
diff --git a/.gitlab/ci/env_fedora43_pvs_studio.sh b/.gitlab/ci/env_fedora43_pvs_studio.sh new file mode 100644 index 0000000..36dba85 --- /dev/null +++ b/.gitlab/ci/env_fedora43_pvs_studio.sh
@@ -0,0 +1 @@ +. .gitlab/ci/pvs-studio-env.sh
diff --git a/.gitlab/ci/pvs-studio-env.sh b/.gitlab/ci/pvs-studio-env.sh new file mode 100644 index 0000000..c179b30 --- /dev/null +++ b/.gitlab/ci/pvs-studio-env.sh
@@ -0,0 +1,13 @@ +.gitlab/ci/pvs-studio.sh +export PATH=$PWD/.gitlab/pvs-studio/bin:$PATH +pvs-studio --version + +if test -f "$CMAKE_CI_PVS_STUDIO_LICENSE_USER" -a -f "$CMAKE_CI_PVS_STUDIO_LICENSE_KEY"; then + pvs-studio-analyzer credentials "$(<"$CMAKE_CI_PVS_STUDIO_LICENSE_USER")" "$(<"$CMAKE_CI_PVS_STUDIO_LICENSE_KEY")" +else + echo "The CMAKE_CI_PVS_STUDIO_LICENSE_USER and CMAKE_CI_PVS_STUDIO_LICENSE_KEY files were not both provided!" + exit 1 +fi +rm -f "$CMAKE_CI_PVS_STUDIO_LICENSE_USER" "$CMAKE_CI_PVS_STUDIO_LICENSE_KEY" +unset CMAKE_CI_PVS_STUDIO_LICENSE_KEY +unset CMAKE_CI_PVS_STUDIO_LICENSE_USER
diff --git a/.gitlab/ci/pvs-studio.sh b/.gitlab/ci/pvs-studio.sh new file mode 100755 index 0000000..b0eddf3 --- /dev/null +++ b/.gitlab/ci/pvs-studio.sh
@@ -0,0 +1,33 @@ +#!/bin/sh + +set -e + +readonly version="7.42.105112.761" + +case "$(uname -s)-$(uname -m)" in + Linux-x86_64) + # https://files.pvs-studio.com/pvs-studio-7.42.105112.761-x86_64.tgz + shatool="sha256sum" + sha256sum="0ced11d17855cc172a860f8cc19a89f10adb6ab7185992fb57474118e9bdabd7" + filename="pvs-studio-$version-x86_64" + ;; + *) + echo "Unrecognized platform $(uname -s)-$(uname -m)" + exit 1 + ;; +esac +readonly shatool +readonly sha256sum + +cd .gitlab + +# This URL is only visible inside of Kitware's network. See above filename table. +baseurl="https://cmake.org/files/dependencies/internal" + +tarball="$filename.tgz" +echo "$sha256sum $tarball" > pvs-studio.sha256sum +curl -OL "$baseurl/$tarball" +$shatool --check pvs-studio.sha256sum +tar xzf "$tarball" +rm "$tarball" pvs-studio.sha256sum +mv "$filename" "pvs-studio"
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index 5e6f1ee..4d9aff3 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml
@@ -58,7 +58,7 @@ ### Debian .debian13: - image: "kitware/cmake:ci-debian13-x86_64-2026-02-08" + image: "kitware/cmake:ci-debian13-x86_64-2026-04-28" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci" @@ -82,7 +82,7 @@ ### Fedora .fedora43: - image: "kitware/cmake:ci-fedora43-x86_64-2026-02-08" + image: "kitware/cmake:ci-fedora43-x86_64-2026-04-28" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci/long file name for testing purposes" @@ -132,6 +132,14 @@ CMAKE_CONFIGURATION: fedora43_tidy CMAKE_CI_NO_INSTALL: 1 +.fedora43_pvs_studio: + extends: .fedora43 + environment: + name: pvs-studio + variables: + CMAKE_CONFIGURATION: fedora43_pvs_studio + CMAKE_CI_NO_INSTALL: 1 + .fedora43_clang_analyzer: extends: .fedora43
diff --git a/.pvsconfig b/.pvsconfig new file mode 100644 index 0000000..4d7413f --- /dev/null +++ b/.pvsconfig
@@ -0,0 +1,100 @@ +# Identical sub-expressions to the left and to the right of 'foo' operator. +//-V::501 +# Pointer to local variable 'X' is stored outside the scope of this variable. Such a pointer will become invalid. +//-V::506 +# The 'new type(n)' pattern was detected. Probably meant: 'new type[n]'. +//-V::508 +# The 'x' variable is assigned values twice successively. Perhaps this is a mistake. +//-V::519 +# Possible null pointer dereference. +//-V::522 +# Constant value is represented by an octal form. +//-V::536 +# Iterators are passed as arguments to 'Foo' function. Consider inspecting the expression. +//-V::539 +# Expression is always true/false. +//-V::547 +# Expression of the 'A - B > 0' kind will work as 'A != B'. +//-V::555 +# Possible array overrun. +//-V::557 +# Part of conditional expression is always true/false. +//-V::560 +# It is suspicious that the argument of sizeof() operator is the expression. +//-V::568 +# Variable is assigned to itself. +//-V::570 +# Recurring check. This condition was already verified in previous line. +//-V::571 +# Function receives suspicious argument. +//-V::575 +# Conditional expressions of 'if' statements located next to each other are identical. +//-V::581 +# The 'Foo' function is called twice to deallocate the same resource. +//-V::586 +# Expression is enclosed by parentheses twice: ((expression)). One pair of parentheses is unnecessary or typo is present. +//-V::592 +# Pointer was used before its check for nullptr. Check lines: N1, N2. +//-V::595 +# Possible division or mod by zero. +//-V::609 +# Condition of a loop is always true/false. +//-V::654 +# Possible meaningless check for null, as memory was allocated using 'new' operator. Memory allocation will lead to an exception. +//-V::668 +# An object is used as an argument to its own method. +//-V::678 +# Pattern A || (A && ...) was detected. The expression is excessive or contains a logical error. +//-V::686 +# The class implements a copy constructor/operator=, but lacks the operator=/copy constructor. +//-V::690 +# Excessive check can be simplified. The '||' operator is surrounded by opposite expressions 'x' and '!x'. +//-V::728 +# Not all members of a class are initialized inside the constructor. +//-V::730 +# Suspicious access to element of '...' array by a constant index inside a loop. +//-V::767 +# The pointer in the expression equals nullptr. The resulting value is meaningless and should not be used. +//-V::769 +# Two similar code fragments were found. +//-V::778 +# Value of a variable is checked after it is used. Possible error in program's logic. Check lines: N1, N2. +//-V::781 +# Variable is assigned but not used by the end of the function. +//-V::1001 +# Pointer was used unsafely after its check for nullptr. +//-V::1004 +# Value from the uninitialized optional is used. It may be an error. +//-V::1007 +# The value is out of range of enum values. This causes unspecified or undefined behavior. +//-V::1016 +# A pointer without owner is added to the container by the 'emplace_back' method. A memory leak will occur in case of an exception. +//-V::1023 +# Possible overflow. Consider casting operands, not the result. +//-V::1028 +# Variable is used after it is moved. +//-V::1030 +# Two or more case-branches perform the same actions. +//-V::1037 +# This file is marked with copyleft license, which requires you to open the derived source code. +//-V::1042 +# Loop break conditions do not depend on the number of iterations. +//-V::1044 +# Variable 'foo' was assigned the same value. +//-V::1048 +# It is possible that an assigned variable should be checked in the next condition. Consider checking for typos. +//-V::1051 +# Calling the 'foo' virtual function in the constructor/destructor may lead to unexpected result at runtime. +//-V::1053 +# Return value is not always used. Consider inspecting the 'foo' function. +//-V::1071 +# Conditional initialization inside the constructor may leave some members uninitialized. +//-V::1077 +# Call of the 'Foo' function will lead to buffer underflow. +//-V::1086 +# Waiting on condition variable without predicate. A thread can wait indefinitely or experience a spurious wake-up. +//-V::1089 +# The 'emplace' / 'insert' function call contains potentially dangerous move operation. Moved object can be destroyed even if there is no insertion. +//-V::1098 +# Preprocessing directive is present within a macro argument. This leads to undefined behavior. +//-V::1119
diff --git a/CMakeLists.txt b/CMakeLists.txt index 06778c7..b391315 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -391,6 +391,22 @@ list(APPEND CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${CMake_IWYU_OPTIONS}) endif() +option(CMake_RUN_PVS_STUDIO "Run pvs-studio-analyzer with the compiler." OFF) +if(CMake_RUN_PVS_STUDIO) + if(CMake_SOURCE_DIR STREQUAL CMake_BINARY_DIR) + message(FATAL_ERROR "CMake_RUN_PVS_STUDIO requires an out-of-source build!") + endif() + find_program(PVS_STUDIO_COMMAND NAMES pvs-studio-analyzer) + if(NOT PVS_STUDIO_COMMAND) + message(FATAL_ERROR "CMake_RUN_PVS_STUDIO is ON but pvs-studio-analyzer is not found!") + endif() + set(CMAKE_CXX_PVS_STUDIO + "${PVS_STUDIO_COMMAND}" analyze + -a GA + --apply-pvs-configs + --project-root "${CMAKE_SOURCE_DIR}" + ) +endif() #----------------------------------------------------------------------- # a macro that only sets the FOLDER target property if it's
diff --git a/Help/command/install.rst b/Help/command/install.rst index 7ed27e7..0d6ccb6 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst
@@ -60,6 +60,11 @@ ``<dir>`` should be a relative path. An absolute path is allowed, but not recommended. + .. versionadded:: 4.4 + The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION <CMD_INSTALL_ABSOLUTE_DESTINATION>` + diagnostic can be enabled to warn or error out when an absolute destination + is provided. + When a relative path is given, it is interpreted relative to the value of the :variable:`CMAKE_INSTALL_PREFIX` variable. The prefix can be relocated at install time using the ``DESTDIR``
diff --git a/Help/manual/cmake-diagnostics.7.rst b/Help/manual/cmake-diagnostics.7.rst index 66a08d5..5612299 100644 --- a/Help/manual/cmake-diagnostics.7.rst +++ b/Help/manual/cmake-diagnostics.7.rst
@@ -88,3 +88,15 @@ Although the action of this warning category can be queried as usual, changes made using the :command:`cmake_diagnostic` command have no effect. + +.. _CMD_INSTALL_ABSOLUTE_DESTINATION: + +``CMD_INSTALL_ABSOLUTE_DESTINATION`` (``-Winstall-absolute-destination``) +------------------------------------------------------------------------- + +:Default: Ignore + +Warn when an :command:`install` command specifies an absolute +``DESTINATION`` path. Absolute destinations are typically undesirable +because they prevent the installation prefix from being overridden at +install time.
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index 85391cd..2ab7290 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst
@@ -100,6 +100,7 @@ .. toctree:: :maxdepth: 1 + CMP0217: The MACROS directory property does not exist anymore. </policy/CMP0217> CMP0216: Swift targets have a default project name. </policy/CMP0216> CMP0215: Ninja generators emit Swift modules separately from compilation. </policy/CMP0215> CMP0214: Honor CMAKE_EXE_LINKER_FLAGS for Swift executable targets. </policy/CMP0214>
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst index 8985502..d5f7c98 100644 --- a/Help/manual/cmake-presets.7.rst +++ b/Help/manual/cmake-presets.7.rst
@@ -486,6 +486,9 @@ * The ``uninitialized`` and ``unusedCli`` fields were added to :preset:`configurePresets.errors`. + * The ``installAbsoluteDestination`` field was added to + :preset:`configurePresets.warnings` and :preset:`configurePresets.errors`. + * Changes to `Macro Expansion`_ * The `${fileDir} <CMakePresets fileDir_>`_ macro now always expands to
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 3638cf6..571585d 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst
@@ -84,7 +84,6 @@ /prop_dir/LINK_DIRECTORIES /prop_dir/LINK_OPTIONS /prop_dir/LISTFILE_STACK - /prop_dir/MACROS /prop_dir/PARENT_DIRECTORY /prop_dir/RULE_LAUNCH_COMPILE /prop_dir/RULE_LAUNCH_CUSTOM @@ -702,6 +701,7 @@ /prop_dir/COMPILE_DEFINITIONS_CONFIG /prop_dir/INTERPROCEDURAL_OPTIMIZATION /prop_dir/INTERPROCEDURAL_OPTIMIZATION_CONFIG + /prop_dir/MACROS /prop_dir/TEST_INCLUDE_FILE
diff --git a/Help/manual/presets/errors-properties.rst b/Help/manual/presets/errors-properties.rst index 3f1216f..7bf5dac 100644 --- a/Help/manual/presets/errors-properties.rst +++ b/Help/manual/presets/errors-properties.rst
@@ -32,6 +32,15 @@ This may not be set to ``true`` if ``warnings.dev`` is set to ``false``. +.. _`CMakePresets.configurePresets.errors.installAbsoluteDestination`: + +``installAbsoluteDestination`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :cmake-option:`-Werror=install-absolute-destination` or + :cmake-option:`-Wno-error=install-absolute-destination` on the command line. + This may not be set to ``true`` if ``warnings.installAbsoluteDestination`` is set to ``false``. + .. _`CMakePresets.configurePresets.errors.uninitialized`: ``uninitialized``
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json index 70f5024..cd52447 100644 --- a/Help/manual/presets/schema.json +++ b/Help/manual/presets/schema.json
@@ -1286,6 +1286,9 @@ "deprecated": { "$ref": "#/definitions/configurePresets.warnings.deprecated@v1.." }, + "installAbsoluteDestination": { + "$ref": "#/definitions/configurePresets.warnings.installAbsoluteDestination@v12" + }, "uninitialized": { "$ref": "#/definitions/configurePresets.warnings.uninitialized@v1.." }, @@ -1312,6 +1315,10 @@ "type": "boolean", "description": "An optional boolean. Equivalent to passing -Wdev or -Wno-dev on the command line. This may not be set to false if errors.dev is set to true." }, + "configurePresets.warnings.installAbsoluteDestination@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Winstall-absolute-destination or -Wno-install-absolute-destination on the command line. This may not be set to false if errors.installAbsoluteDestination is set to true." + }, "configurePresets.warnings.uninitialized@v1..": { "type": "boolean", "description": "An optional boolean. Equivalent to passing -Wuninitialized or -Wno-uninitialized on the command line. This may not be set to false if errors.uninitialized is set to true." @@ -1364,6 +1371,9 @@ "deprecated": { "$ref": "#/definitions/configurePresets.errors.deprecated@v1.." }, + "installAbsoluteDestination": { + "$ref": "#/definitions/configurePresets.errors.installAbsoluteDestination@v12" + }, "uninitialized": { "$ref": "#/definitions/configurePresets.errors.uninitialized@v12" }, @@ -1387,6 +1397,10 @@ "type": "boolean", "description": "An optional boolean. Equivalent to passing -Werror=dev or -Wno-error=dev on the command line. This may not be set to true if warnings.dev is set to false." }, + "configurePresets.errors.installAbsoluteDestination@v12": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=install-absolute-destination or -Wno-error=install-absolute-destination on the command line. This may not be set to true if warnings.installAbsoluteDestination is set to false." + }, "configurePresets.errors.uninitialized@v12": { "type": "boolean", "description": "An optional boolean. Equivalent to passing -Werror=uninitialized or -Wno-error=uninitialized on the command line. This may not be set to true if warnings.uninitialized is set to false."
diff --git a/Help/manual/presets/warnings-properties.rst b/Help/manual/presets/warnings-properties.rst index acfce8f..0a31190 100644 --- a/Help/manual/presets/warnings-properties.rst +++ b/Help/manual/presets/warnings-properties.rst
@@ -32,6 +32,15 @@ This may not be set to ``false`` if ``errors.dev`` is set to ``true``. +.. _`CMakePresets.configurePresets.warnings.installAbsoluteDestination`: + +``installAbsoluteDestination`` + .. presets-versionadded:: 12 + + An optional boolean. Equivalent to passing :option:`-Winstall-absolute-destination <cmake -W>` or + :option:`-Wno-install-absolute-destination <cmake -Wno->` on the command line. + This may not be set to ``false`` if ``errors.installAbsoluteDestination`` is set to ``true``. + .. _`CMakePresets.configurePresets.warnings.uninitialized`: ``uninitialized``
diff --git a/Help/policy/CMP0217.rst b/Help/policy/CMP0217.rst new file mode 100644 index 0000000..052b82c6 --- /dev/null +++ b/Help/policy/CMP0217.rst
@@ -0,0 +1,24 @@ +CMP0217 +------- + +.. versionadded:: 4.4 + +The :prop_dir:`MACROS` directory property does not exist anymore. + +In CMake 4.3 and below, the macros defined in a directory are registered in the +:prop_dir:`MACROS` directory property. However, the property does not have the +same scope as macros, so it does not represent the actual list of macros. +In CMake 4.4 and above, this property is not longer filled with the defined +macros. One may still use :command:`if(COMMAND myMacro) <if(COMMAND)>` to test +whether one exists. + +The ``OLD`` behavior for this policy is to store macros definition in the +:prop_dir:`MACROS` directory property. +The ``NEW`` behavior for this policy is to no longer manage the +:prop_dir:`MACROS` directory property. + +.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.4 +.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn +.. include:: include/STANDARD_ADVICE.rst + +.. include:: include/DEPRECATED.rst
diff --git a/Help/prop_dir/MACROS.rst b/Help/prop_dir/MACROS.rst index 245cc1b..7f1a7c5 100644 --- a/Help/prop_dir/MACROS.rst +++ b/Help/prop_dir/MACROS.rst
@@ -1,8 +1,4 @@ MACROS ------ -List of macro commands available in the current directory. - -This read-only property specifies the list of CMake macros currently -defined. It is intended for debugging purposes. See the :command:`macro` -command. +This directory property does not exist anymore. See policy :policy:`CMP0217`.
diff --git a/Help/release/dev/MACROS-directory-property.rst b/Help/release/dev/MACROS-directory-property.rst new file mode 100644 index 0000000..314310b --- /dev/null +++ b/Help/release/dev/MACROS-directory-property.rst
@@ -0,0 +1,5 @@ +MACROS-directory-property +------------------------- + +* The :prop_dir:`MACROS` directory property does not exist anymore. See policy + :policy:`CMP0217`.
diff --git a/Help/release/dev/install-absolute-dest-configure-time.rst b/Help/release/dev/install-absolute-dest-configure-time.rst new file mode 100644 index 0000000..25a9c0a --- /dev/null +++ b/Help/release/dev/install-absolute-dest-configure-time.rst
@@ -0,0 +1,10 @@ +install-absolute-dest-configure-time +------------------------------------- + +* CMake gained the ability to diagnose :command:`install` commands that + specify an absolute ``DESTINATION`` path via the + :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION <CMD_INSTALL_ABSOLUTE_DESTINATION>` + diagnostic category. This diagnostic may be controlled with the + :option:`-Winstall-absolute-destination <cmake -W>` command-line option, the + ``installAbsoluteDestination`` field in a :manual:`cmake-presets(7)` + ``warnings`` object, or the :command:`cmake_diagnostic` command.
diff --git a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst index 38e9b7b..84a27e5 100644 --- a/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst +++ b/Help/variable/CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION.rst
@@ -8,3 +8,11 @@ file takes place. This variable is used by CMake-generated ``cmake_install.cmake`` scripts. If one sets this variable to ``ON`` while running the script, it may get fatal error messages from the script. + +.. versionadded:: 4.4 + + The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION <CMD_INSTALL_ABSOLUTE_DESTINATION>` + diagnostic can be used to to generate errors for absolute install destinations + at generate time. + +See also :variable:`CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION`.
diff --git a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst index 81c1158..fac680e 100644 --- a/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst +++ b/Help/variable/CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION.rst
@@ -7,3 +7,11 @@ This variable is used by CMake-generated ``cmake_install.cmake`` scripts. If one sets this variable to ``ON`` while running the script, it may get warning messages from the script. + +.. versionadded:: 4.4 + + The :ref:`CMD_INSTALL_ABSOLUTE_DESTINATION <CMD_INSTALL_ABSOLUTE_DESTINATION>` + diagnostic can be used to to generate warnings for absolute install destinations + at generate time. + +See also :variable:`CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION`.
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake index 8a6e1fb..73e30d2 100644 --- a/Modules/FindCUDAToolkit.cmake +++ b/Modules/FindCUDAToolkit.cmake
@@ -1539,10 +1539,13 @@ _CUDAToolkit_find_and_add_import_lib( sanitizer ONLY_SEARCH_FOR sanitizer-public - LIBRARY_SEARCH_DIRS - "${CUDAToolkit_LIBRARY_ROOT}/compute-sanitizer" - "${CUDAToolkit_LIBRARY_ROOT}/Sanitizer" - "${CUDAToolkit_LIBRARY_ROOT}/extras/Sanitizer" + EXTRA_PATH_SUFFIXES + "../compute-sanitizer" + "../../../compute-sanitizer" + "../Sanitizer" + "../../../Sanitizer" + "../extras/Sanitizer" + "../../../extras/Sanitizer" EXTRA_INCLUDE_DIRS "${CUDAToolkit_CUPTI_INCLUDE_DIR}" ) if(TARGET CUDA::sanitizer)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index df39ab1..ecb5c31 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 4) set(CMake_VERSION_MINOR 3) -set(CMake_VERSION_PATCH 20260430) +set(CMake_VERSION_PATCH 20260501) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0)
diff --git a/Source/cmDiagnostics.h b/Source/cmDiagnostics.h index 3ab2239..bad732b 100644 --- a/Source/cmDiagnostics.h +++ b/Source/cmDiagnostics.h
@@ -28,6 +28,7 @@ #define CM_FOR_EACH_DIAGNOSTIC_TABLE(ACTION, SELECT) \ SELECT(ACTION, Warn, CMD_NONE, CMD_AUTHOR, 12) \ SELECT(ACTION, Warn, CMD_AUTHOR, CMD_DEPRECATED, 1) \ + SELECT(ACTION, Ignore, CMD_AUTHOR, CMD_INSTALL_ABSOLUTE_DESTINATION, 12) \ SELECT(ACTION, Ignore, CMD_NONE, CMD_UNINITIALIZED, 1) \ SELECT(ACTION, Warn, CMD_NONE, CMD_UNUSED_CLI, 1)
diff --git a/Source/cmExportInstallCMakeConfigGenerator.cxx b/Source/cmExportInstallCMakeConfigGenerator.cxx index eae42b6..2f87099 100644 --- a/Source/cmExportInstallCMakeConfigGenerator.cxx +++ b/Source/cmExportInstallCMakeConfigGenerator.cxx
@@ -284,22 +284,21 @@ auto configs = gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); - cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); - auto cge = - ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination()); - for (auto const& config : configs) { - auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte); - auto dest = cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + cmInstallFileSetGenerator::DestinationContext result = + te->FileSetGenerators.at(fileSet->GetName()) + ->GetDestination(gte, config); + + std::string dest = cmOutputConverter::EscapeForCMake( + result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap); + if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) { dest = cmStrCat("${_IMPORT_PREFIX}/", dest); } auto const& type = fileSet->GetType(); // C++ modules do not support interface file sets which are dependent upon // the configuration. - if (cge->GetHadContextSensitiveCondition() && + if (result.HadContextSensitiveCondition && type == cm::FileSetMetadata::CXX_MODULES) { auto* mf = this->IEGen->GetLocalGenerator()->GetMakefile(); std::ostringstream e; @@ -311,7 +310,7 @@ return std::string{}; } - if (cge->GetHadContextSensitiveCondition() && configs.size() != 1) { + if (result.HadContextSensitiveCondition && configs.size() != 1) { resultVector.push_back( cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\"")); } else {
diff --git a/Source/cmExportInstallPackageInfoGenerator.cxx b/Source/cmExportInstallPackageInfoGenerator.cxx index 78eb146..202594a 100644 --- a/Source/cmExportInstallPackageInfoGenerator.cxx +++ b/Source/cmExportInstallPackageInfoGenerator.cxx
@@ -224,18 +224,14 @@ cmGeneratorTarget* gte, cmTargetExport const* te, cmGeneratorFileSet const* fileSet, cm::optional<std::string> const& config) { - cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); - auto cge = - ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination()); + cmInstallFileSetGenerator::DestinationContext result = + te->FileSetGenerators.at(fileSet->GetName()) + ->GetDestination(gte, config.value_or("")); - std::string const unescapedDest = - cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte); - bool const isConfigDependent = cge->GetHadContextSensitiveCondition(); - - if (config && !isConfigDependent) { + if (config && !result.HadContextSensitiveCondition) { return {}; } - if (!config && isConfigDependent) { + if (!config && result.HadContextSensitiveCondition) { this->RequiresConfigFiles = true; return {}; } @@ -255,9 +251,9 @@ } cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); + result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) { dest = cmStrCat("@prefix@/"_s, *dest); }
diff --git a/Source/cmExportInstallSbomGenerator.cxx b/Source/cmExportInstallSbomGenerator.cxx index d944383..f9641a1 100644 --- a/Source/cmExportInstallSbomGenerator.cxx +++ b/Source/cmExportInstallSbomGenerator.cxx
@@ -216,15 +216,11 @@ cmGeneratorTarget* gte, cmTargetExport const* te, cmGeneratorFileSet const* fileSet, cm::optional<std::string> const& config) { - cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); - auto cge = - ge.Parse(te->FileSetGenerators.at(fileSet->GetName())->GetDestination()); + cmInstallFileSetGenerator::DestinationContext result = + te->FileSetGenerators.at(fileSet->GetName()) + ->GetDestination(gte, config.value_or("")); - std::string const unescapedDest = - cge->Evaluate(gte->LocalGenerator, config.value_or(""), gte); - bool const isConfigDependent = cge->GetHadContextSensitiveCondition(); - - if (config && !isConfigDependent) { + if (config && !result.HadContextSensitiveCondition) { return {}; } @@ -241,9 +237,9 @@ } cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake( - unescapedDest, cmOutputConverter::WrapQuotes::NoWrap); + result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap); - if (!cmSystemTools::FileIsFullPath(unescapedDest)) { + if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) { dest = cmStrCat("@prefix@/"_s, *dest); }
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx index 3e98e3f..e9e8dfd 100644 --- a/Source/cmFindBase.cxx +++ b/Source/cmFindBase.cxx
@@ -184,18 +184,16 @@ } // ensure a macro is not specified as validator auto const& validatorName = args[j]; - cmList macros{ this->Makefile->GetProperty("MACROS") }; - if (std::find_if(macros.begin(), macros.end(), - [&validatorName](std::string const& item) { - return cmSystemTools::Strucmp(validatorName.c_str(), - item.c_str()) == 0; - }) != macros.end()) { + if (this->Makefile->GetState() + ->GetCommandType(validatorName) + .value_or(cmStateEnums::CommandType::Macro) != + cmStateEnums::CommandType::Function) { this->SetError(cmStrCat( "command specified for VALIDATOR is not a function: ", args[j], '.')); return false; } - this->ValidatorName = args[j]; + this->ValidatorName = validatorName; } else if (this->CheckCommonArgument(args[j])) { doing = DoingNone; } else {
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx index 60c3b90..456cb72 100644 --- a/Source/cmFunctionCommand.cxx +++ b/Source/cmFunctionCommand.cxx
@@ -19,6 +19,7 @@ #include "cmPolicies.h" #include "cmRange.h" #include "cmState.h" +#include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -175,7 +176,7 @@ mf.RecordPolicies(f.Policies); mf.RecordDiagnostics(f.Diagnostics); return mf.GetState()->AddScriptedCommand( - this->Args.front(), + this->Args.front(), cmStateEnums::CommandType::Function, BT<cmState::Command>(std::move(f), mf.GetBacktrace().Push(this->GetStartingContext())), mf);
diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx index 7497afa..db8f8ec 100644 --- a/Source/cmGetCMakePropertyCommand.cxx +++ b/Source/cmGetCMakePropertyCommand.cxx
@@ -8,6 +8,7 @@ #include "cmGlobalGenerator.h" #include "cmList.h" #include "cmMakefile.h" +#include "cmPolicies.h" #include "cmState.h" #include "cmValue.h" @@ -27,7 +28,9 @@ if (cmValue varsProp = status.GetMakefile().GetProperty("VARIABLES")) { output = *varsProp; } - } else if (args[1] == "MACROS") { + } else if (args[1] == "MACROS" && + status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) != + cmPolicies::NEW) { output.clear(); if (cmValue macrosProp = status.GetMakefile().GetProperty("MACROS")) { output = *macrosProp;
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 1fb9d4b..8137d69 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx
@@ -1912,6 +1912,15 @@ cmInstallGenerator::MessageLevel message = cmInstallGenerator::SelectMessageLevel(helper.Makefile, message_never); + // Check for an absolute destination. + if (cmGeneratorExpression::Find(*destination) == std::string::npos && + cmSystemTools::FileIsFullPath(*destination)) { + helper.Makefile->IssueDiagnostic( + cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION, + cmStrCat("INSTALL command given absolute DESTINATION path (", + *destination, ").\n")); + } + // Create the directory install generator. helper.Makefile->AddInstallGenerator( cm::make_unique<cmInstallDirectoryGenerator>(
diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 7bf861e..c8b1ad9 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h
@@ -22,7 +22,7 @@ this->GenericArguments = args; } - // Compute destination path.and check permissions + // Compute destination path and check permissions. bool Finalize(); std::string const& GetDestination() const;
diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx index cf1fac9..bd62dd3 100644 --- a/Source/cmInstallDirectoryGenerator.cxx +++ b/Source/cmInstallDirectoryGenerator.cxx
@@ -118,6 +118,9 @@ std::string cmInstallDirectoryGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate(this->Destination, - this->LocalGenerator, config); + std::string dest = cmGeneratorExpression::Evaluate( + this->Destination, this->LocalGenerator, config); + cmInstallGenerator::CheckAbsoluteDestination(dest, this->LocalGenerator, + this->Backtrace); + return dest; }
diff --git a/Source/cmInstallFileSetGenerator.cxx b/Source/cmInstallFileSetGenerator.cxx index 85b8275..e6e9f03 100644 --- a/Source/cmInstallFileSetGenerator.cxx +++ b/Source/cmInstallFileSetGenerator.cxx
@@ -4,6 +4,7 @@ #include <algorithm> #include <map> +#include <memory> #include <string> #include <utility> #include <vector> @@ -21,6 +22,7 @@ #include "cmList.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" +#include "cmMakefile.h" #include "cmMessageType.h" #include "cmStringAlgorithms.h" #include "cmTarget.h" @@ -91,11 +93,32 @@ return true; } +std::string cmInstallFileSetGenerator::GetDestination() const +{ + cmInstallGenerator::CheckAbsoluteDestination( + this->Destination, this->LocalGenerator, this->Backtrace); + return this->Destination; +} + std::string cmInstallFileSetGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate(this->Destination, - this->LocalGenerator, config); + return this->GetDestination(this->Target, config).UnescapedDestination; +} + +cmInstallFileSetGenerator::DestinationContext +cmInstallFileSetGenerator::GetDestination(cmGeneratorTarget* gte, + std::string const& config) const +{ + cmGeneratorExpression ge(*gte->Makefile->GetCMakeInstance()); + std::unique_ptr<cmCompiledGeneratorExpression> cge = + ge.Parse(this->Destination); + + std::string const dest = cge->Evaluate(gte->LocalGenerator, config, gte); + cmInstallGenerator::CheckAbsoluteDestination(dest, gte->LocalGenerator, + this->Backtrace); + + return { dest, cge->GetHadContextSensitiveCondition() }; } void cmInstallFileSetGenerator::GenerateScriptForConfig(
diff --git a/Source/cmInstallFileSetGenerator.h b/Source/cmInstallFileSetGenerator.h index ff56201..b0488fb 100644 --- a/Source/cmInstallFileSetGenerator.h +++ b/Source/cmInstallFileSetGenerator.h
@@ -9,8 +9,9 @@ #include "cmInstallGenerator.h" -class cmGeneratorTarget; +class cmExportInstallCMakeConfigGenerator; class cmGeneratorFileSet; +class cmGeneratorTarget; class cmListFileBacktrace; class cmLocalGenerator; @@ -28,14 +29,23 @@ bool Compute(cmLocalGenerator* lg) override; + struct DestinationContext + { + std::string UnescapedDestination; + bool HadContextSensitiveCondition; + }; std::string GetDestination(std::string const& config) const; - std::string GetDestination() const { return this->Destination; } + DestinationContext GetDestination(cmGeneratorTarget* gt, + std::string const& config) const; bool GetOptional() const { return this->Optional; } std::string GetFileSetName() const { return this->FileSetName; } cmGeneratorFileSet const* GetFileSet() const { return this->FileSet; }; cmGeneratorTarget* GetTarget() const { return this->Target; } protected: + friend cmExportInstallCMakeConfigGenerator; + std::string GetDestination() const; + void GenerateScriptForConfig(std::ostream& os, std::string const& config, Indent indent) override;
diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx index 6da1862..f5613fa 100644 --- a/Source/cmInstallFilesGenerator.cxx +++ b/Source/cmInstallFilesGenerator.cxx
@@ -54,8 +54,11 @@ std::string cmInstallFilesGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate(this->Destination, - this->LocalGenerator, config); + std::string dest = cmGeneratorExpression::Evaluate( + this->Destination, this->LocalGenerator, config); + cmInstallGenerator::CheckAbsoluteDestination(dest, this->LocalGenerator, + this->Backtrace); + return dest; } std::string cmInstallFilesGenerator::GetRename(std::string const& config) const
diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx index dd9ade7..9317f94 100644 --- a/Source/cmInstallGenerator.cxx +++ b/Source/cmInstallGenerator.cxx
@@ -7,6 +7,9 @@ #include <cm/string_view> +#include "cmDiagnostics.h" +#include "cmListFileCache.h" +#include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -229,6 +232,19 @@ return result; } +void cmInstallGenerator::CheckAbsoluteDestination( + std::string const& dest, cmLocalGenerator* lg, cmListFileBacktrace const& bt) +{ + if (!cmSystemTools::FileIsFullPath(dest)) { + return; + } + lg->IssueDiagnostic( + cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION, + cmStrCat("INSTALL command given absolute DESTINATION path (", dest, + ").\n"), + bt); +} + cmInstallGenerator::MessageLevel cmInstallGenerator::SelectMessageLevel( cmMakefile* mf, bool never) {
diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h index 572d192..d35a4b6 100644 --- a/Source/cmInstallGenerator.h +++ b/Source/cmInstallGenerator.h
@@ -56,6 +56,9 @@ /** Get the install destination as it should appear in the installation script. */ static std::string ConvertToAbsoluteDestination(std::string const& dest); + static void CheckAbsoluteDestination(std::string const& dest, + cmLocalGenerator* lg, + cmListFileBacktrace const& bt); /** Test if this generator installs something for a given configuration. */ bool InstallsForConfig(std::string const& config);
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index 6b89f86..e2e7924 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx
@@ -479,8 +479,11 @@ std::string cmInstallTargetGenerator::GetDestination( std::string const& config) const { - return cmGeneratorExpression::Evaluate( - this->Destination, this->Target->GetLocalGenerator(), config); + cmLocalGenerator* lg = this->Target->GetLocalGenerator(); + std::string dest = + cmGeneratorExpression::Evaluate(this->Destination, lg, config); + cmInstallGenerator::CheckAbsoluteDestination(dest, lg, this->Backtrace); + return dest; } std::string cmInstallTargetGenerator::GetInstallFilename(
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx index f193bce..ae825ef 100644 --- a/Source/cmMacroCommand.cxx +++ b/Source/cmMacroCommand.cxx
@@ -19,6 +19,7 @@ #include "cmPolicies.h" #include "cmRange.h" #include "cmState.h" +#include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -170,7 +171,11 @@ cmExecutionStatus& status) { cmMakefile& mf = status.GetMakefile(); - mf.AppendProperty("MACROS", this->Args[0]); + if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) != + cmPolicies::NEW) { + + mf.AppendProperty("MACROS", this->Args[0]); + } // create a new command and add it to cmake cmMacroHelperCommand f; f.Args = this->Args; @@ -179,7 +184,7 @@ mf.RecordPolicies(f.Policies); mf.RecordDiagnostics(f.Diagnostics); return mf.GetState()->AddScriptedCommand( - this->Args[0], + this->Args[0], cmStateEnums::CommandType::Macro, BT<cmState::Command>(std::move(f), mf.GetBacktrace().Push(this->GetStartingContext())), mf);
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index c9ce1df..c962997 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -1652,6 +1652,14 @@ } } + // For split Swift builds, ensure the link edge depends on the target's own + // .swiftmodule so the emit-module edge runs even when no other target in + // the build depends on it (e.g. install-only targets). + std::string swiftModuleOutput = this->GetSwiftModuleOutput(config); + if (!swiftModuleOutput.empty()) { + linkBuild.ImplicitDeps.emplace_back(std::move(swiftModuleOutput)); + } + // Ninja should restat after linking if and only if there are byproducts. vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1";
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx index 12ecbee..762da6c 100644 --- a/Source/cmNinjaTargetGenerator.cxx +++ b/Source/cmNinjaTargetGenerator.cxx
@@ -1438,6 +1438,7 @@ this->Configs[config].SwiftOutputMap[""] = deps; cmGeneratedFileStream output(mapFilePath); + output.SetCopyIfDifferent(true); output << this->Configs[config].SwiftOutputMap; // Add flag @@ -2202,6 +2203,9 @@ // Importable targets keep -emit-module on compile so swiftc still emits // .swiftdoc. When splitting module emission, both the .swiftmodule output // and -emit-module flags move entirely to the separate emit-module edge. + if (targetIsImportable) { + this->Configs[config].SwiftModuleOutput = moduleFilepath; + } if (targetIsImportable && !emitModuleSeparately) { objBuild.Outputs.push_back(moduleFilepath); } @@ -2276,11 +2280,6 @@ objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config)); - // Write object build - this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), - objBuild, - this->ForceResponseFile() ? -1 : 0); - // Write a separate emit-module build edge that produces .swiftmodule // without compile outputs. This allows downstream Swift targets to start // compiling as soon as the module interface is ready, overlapping with @@ -2311,7 +2310,16 @@ this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), modBuild, this->ForceResponseFile() ? -1 : 0); + + // Both edges share the same -output-file-map; serialize the compile + // edge after emit-module so they do not race on the module .swiftdeps. + objBuild.OrderOnlyDeps.push_back(moduleFilepath); } + + // Write object build + this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig), + objBuild, + this->ForceResponseFile() ? -1 : 0); } void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang, @@ -2673,6 +2681,16 @@ return {}; } +std::string cmNinjaTargetGenerator::GetSwiftModuleOutput( + std::string const& config) const +{ + auto const it = this->Configs.find(config); + if (it != this->Configs.end()) { + return it->second.SwiftModuleOutput; + } + return {}; +} + void cmNinjaTargetGenerator::EnsureDirectoryExists( std::string const& path) const {
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h index c706e75..41f3f7c 100644 --- a/Source/cmNinjaTargetGenerator.h +++ b/Source/cmNinjaTargetGenerator.h
@@ -216,6 +216,7 @@ void AdditionalCleanFiles(std::string const& config); cmNinjaDeps GetObjects(std::string const& config) const; + std::string GetSwiftModuleOutput(std::string const& config) const; void EnsureDirectoryExists(std::string const& dir) const; void EnsureParentDirectoryExists(std::string const& path) const; @@ -278,6 +279,8 @@ Json::Value SwiftOutputMap; cmNinjaDeps ExtraFiles; std::unique_ptr<MacOSXContentGeneratorType> MacOSXContentGenerator; + // Path declared as the .swiftmodule output (compile or emit-module edge). + std::string SwiftModuleOutput; }; std::map<std::string, ByConfig> Configs;
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 57d2597..1ef7f10 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h
@@ -647,7 +647,10 @@ "Ninja generators emit Swift modules separately from compilation.", \ 4, 4, 0, WARN) \ SELECT(POLICY, CMP0216, "Swift targets have a default project name.", 4, 4, \ - 0, WARN) + 0, WARN) \ + SELECT(POLICY, CMP0217, \ + "The MACROS directory property does not exist anymore.", 4, 4, 0, \ + WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) #define CM_FOR_EACH_POLICY_ID(POLICY) \ @@ -738,14 +741,15 @@ /** \brief Always the last entry. * - * Useful mostly to avoid adding a comma the last policy when adding a new - * one. + * Useful mostly to avoid adding a comma the last policy when adding + * a new one. */ CMPCOUNT }; //! convert a string policy ID into a number - static bool GetPolicyID(char const* id, /* out */ cmPolicies::PolicyID& pid); + static bool GetPolicyID(char const* id, + /* out */ cmPolicies::PolicyID& pid); //! Return whether a policy has been removed. static bool IsRemoved(cmPolicies::PolicyID id);
diff --git a/Source/cmSetDirectoryPropertiesCommand.cxx b/Source/cmSetDirectoryPropertiesCommand.cxx index cdfd068..024c76b 100644 --- a/Source/cmSetDirectoryPropertiesCommand.cxx +++ b/Source/cmSetDirectoryPropertiesCommand.cxx
@@ -4,6 +4,7 @@ #include "cmExecutionStatus.h" #include "cmMakefile.h" +#include "cmPolicies.h" // cmSetDirectoryPropertiesCommand bool cmSetDirectoryPropertiesCommand(std::vector<std::string> const& args, @@ -27,7 +28,9 @@ "Variables and cache variables should be set using SET command"); return false; } - if (prop == "MACROS") { + if (prop == "MACROS" && + status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0217) != + cmPolicies::NEW) { status.SetError( "Commands and macros cannot be set using SET_CMAKE_PROPERTIES"); return false;
diff --git a/Source/cmState.cxx b/Source/cmState.cxx index e458b4c..ecfc0a2 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx
@@ -414,7 +414,10 @@ { assert(name == cmSystemTools::LowerCase(name)); assert(this->BuiltinCommands.find(name) == this->BuiltinCommands.end()); - this->BuiltinCommands.emplace(name, std::move(command)); + this->BuiltinCommands.emplace( + name, + CommandDescriptor{ cmStateEnums::CommandType::Function, + std::move(command) }); } static bool InvokeBuiltinCommand(cmState::BuiltinCommand command, @@ -522,8 +525,31 @@ this->AddUnexpectedCommand(name, error); } -bool cmState::AddScriptedCommand(std::string const& name, BT<Command> command, - cmMakefile& mf) +cmState::CommandDescriptor::CommandDescriptor(CommandType type, + Command command) + : Type(type) + , Script(std::move(command)) +{ +} +cmState::CommandDescriptor::CommandDescriptor( + CommandDescriptor&& descriptor) noexcept + : Type(descriptor.Type) + , Script(std::move(descriptor.Script)) +{ +} + +cmState::CommandDescriptor& cmState::CommandDescriptor::operator=( + CommandDescriptor&& descriptor) noexcept +{ + this->Type = descriptor.Type; + this->Script = std::move(descriptor.Script); + + return *this; +} + +bool cmState::AddScriptedCommand(std::string const& name, + cmStateEnums::CommandType type, + BT<Command> command, cmMakefile& mf) { std::string sName = cmSystemTools::LowerCase(name); @@ -538,30 +564,60 @@ } // if the command already exists, give a new name to the old command. - if (Command oldCmd = this->GetCommandByExactName(sName)) { - this->ScriptedCommands["_" + sName] = oldCmd; + if (CommandDescriptor const* oldCmd = + this->GetCommandDescriptorByExactName(sName)) { + this->ScriptedCommands["_" + sName] = *oldCmd; } - this->ScriptedCommands[sName] = std::move(command.Value); + this->ScriptedCommands[sName] = + CommandDescriptor{ type, std::move(command.Value) }; return true; } +cmState::CommandDescriptor const* cmState::GetCommandDescriptorByExactName( + std::string const& name) const +{ + auto pos = this->ScriptedCommands.find(name); + if (pos != this->ScriptedCommands.end()) { + return &pos->second; + } + pos = this->BuiltinCommands.find(name); + if (pos != this->BuiltinCommands.end()) { + return &pos->second; + } + return nullptr; +} + cmState::Command cmState::GetCommand(std::string const& name) const { return this->GetCommandByExactName(cmSystemTools::LowerCase(name)); } +cm::optional<cmStateEnums::CommandType> cmState::GetCommandType( + std::string const& name) const +{ + return this->GetCommandTypeByExactName(cmSystemTools::LowerCase(name)); +} cmState::Command cmState::GetCommandByExactName(std::string const& name) const { - auto pos = this->ScriptedCommands.find(name); - if (pos != this->ScriptedCommands.end()) { - return pos->second; + CommandDescriptor const* descriptor = + this->GetCommandDescriptorByExactName(name); + if (!descriptor) { + return nullptr; } - pos = this->BuiltinCommands.find(name); - if (pos != this->BuiltinCommands.end()) { - return pos->second; + + return descriptor->Script; +} +cm::optional<cmStateEnums::CommandType> cmState::GetCommandTypeByExactName( + std::string const& name) const +{ + CommandDescriptor const* descriptor = + this->GetCommandDescriptorByExactName(name); + if (!descriptor) { + return cm::nullopt; } - return nullptr; + + return descriptor->Type; } std::vector<std::string> cmState::GetCommandNames() const
diff --git a/Source/cmState.h b/Source/cmState.h index a25342f..5d8a1e7 100644 --- a/Source/cmState.h +++ b/Source/cmState.h
@@ -172,8 +172,14 @@ // Returns a command from its name, case insensitive, or nullptr Command GetCommand(std::string const& name) const; + // Returns a command type from its name, case insensitive, or cm::nullopt + cm::optional<cmStateEnums::CommandType> GetCommandType( + std::string const& name) const; // Returns a command from its name, or nullptr Command GetCommandByExactName(std::string const& name) const; + // Returns a command type from its name, or cm::nullopt + cm::optional<cmStateEnums::CommandType> GetCommandTypeByExactName( + std::string const& name) const; void AddBuiltinCommand(std::string const& name, Command command); void AddBuiltinCommand(std::string const& name, BuiltinCommand command); @@ -186,7 +192,8 @@ void AddUnexpectedCommand(std::string const& name, char const* error); void AddUnexpectedFlowControlCommand(std::string const& name, char const* error); - bool AddScriptedCommand(std::string const& name, BT<Command> command, + bool AddScriptedCommand(std::string const& name, + cmStateEnums::CommandType type, BT<Command> command, cmMakefile& mf); void RemoveBuiltinCommand(std::string const& name); void RemoveUserDefinedCommands(); @@ -277,8 +284,32 @@ cmPropertyDefinitionMap PropertyDefinitions; std::vector<std::string> EnabledLanguages; - std::unordered_map<std::string, Command> BuiltinCommands; - std::unordered_map<std::string, Command> ScriptedCommands; + + class CommandDescriptor + { + public: + using CommandType = cmStateEnums::CommandType; + + CommandDescriptor() + : Type(CommandType::Macro) + , Script(nullptr) + { + } + CommandDescriptor(CommandType type, Command command); + CommandDescriptor(CommandDescriptor&& descriptor) noexcept; + + CommandDescriptor& operator=(CommandDescriptor&& descriptor) noexcept; + CommandDescriptor& operator=(CommandDescriptor const& descriptor) = + default; + + CommandType Type; + Command Script; + }; + CommandDescriptor const* GetCommandDescriptorByExactName( + std::string const& name) const; + + std::unordered_map<std::string, CommandDescriptor> BuiltinCommands; + std::unordered_map<std::string, CommandDescriptor> ScriptedCommands; std::unordered_set<std::string> FlowControlCommands; cmPropertyMap GlobalProperties; std::unique_ptr<cmCacheManager> CacheManager;
diff --git a/Source/cmStateTypes.h b/Source/cmStateTypes.h index 6e2d456..1b7043f 100644 --- a/Source/cmStateTypes.h +++ b/Source/cmStateTypes.h
@@ -41,6 +41,12 @@ UNWINDING }; +enum class CommandType +{ + Macro, + Function +}; + // There are multiple overlapping ranges represented here. Be aware that adding // a value to this enumeration may cause failures in numerous places which // assume details about the ordering.
diff --git a/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake b/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake new file mode 100644 index 0000000..881d941 --- /dev/null +++ b/Tests/RunCMake/CMP0217/CMP0217-NEW.cmake
@@ -0,0 +1,10 @@ + +cmake_policy(SET CMP0217 NEW) + +macro(FOO) +endmacro() + +get_property(macros DIRECTORY PROPERTY MACROS) +if (macros) + set(RunCMake_TEST_FAILED "unexpected content for MACROS directory property.") +endif()
diff --git a/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake b/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake new file mode 100644 index 0000000..dee0cc3 --- /dev/null +++ b/Tests/RunCMake/CMP0217/CMP0217-OLD.cmake
@@ -0,0 +1,10 @@ + +cmake_policy(SET CMP0217 OLD) + +macro(FOO) +endmacro() + +get_property(macros DIRECTORY PROPERTY MACROS) +if (NOT "FOO" IN_LIST macros) + set(RunCMake_TEST_FAILED "unexpected content for MACROS directory property.") +endif()
diff --git a/Tests/RunCMake/CMP0217/CMakeLists.txt b/Tests/RunCMake/CMP0217/CMakeLists.txt new file mode 100644 index 0000000..955802c --- /dev/null +++ b/Tests/RunCMake/CMP0217/CMakeLists.txt
@@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 4.0) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0217/RunCMakeTest.cmake b/Tests/RunCMake/CMP0217/RunCMakeTest.cmake new file mode 100644 index 0000000..8b6cffa --- /dev/null +++ b/Tests/RunCMake/CMP0217/RunCMakeTest.cmake
@@ -0,0 +1,4 @@ +include(RunCMake) + +run_cmake(CMP0217-OLD) +run_cmake(CMP0217-NEW)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index b763eb7..6261947 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt
@@ -184,6 +184,7 @@ if(WIN32) add_RunCMake_test(CMP0212) endif() +add_RunCMake_test(CMP0217) if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") add_RunCMake_test(CMP0194 -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION})
diff --git a/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt new file mode 100644 index 0000000..6d9315e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath-stderr.txt
@@ -0,0 +1,5 @@ +CMake Warning \(unused-cli\): + Manually-specified variables were not used by the project: + + RunCMake_GENERATOR + UNUSED_VARIABLE$
diff --git a/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake new file mode 100644 index 0000000..d6fe2ff --- /dev/null +++ b/Tests/RunCMake/CMakePresets/ErrorAbsInstallPath.cmake
@@ -0,0 +1,6 @@ +cmake_diagnostic(GET CMD_INSTALL_ABSOLUTE_DESTINATION action) +if(NOT "${action}" STREQUAL SEND_ERROR) + message(SEND_ERROR + "wrong action for diagnostic CMD_INSTALL_ABSOLUTE_DESTINATION" + " (expected 'SEND_ERROR', actual '${action}')") +endif()
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake index d7a0eb6..8ca58a2 100644 --- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -416,7 +416,9 @@ set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Warnings12.json.in") run_cmake_presets(NoWarningFlags) run_cmake_presets(WarningFlags) +run_cmake_presets(WarningAbsInstallPath) run_cmake_presets(DisableWarningFlags) +run_cmake_presets(ErrorAbsInstallPath) run_cmake_presets(ErrorDev) run_cmake_presets(ErrorUninitialized) run_cmake_presets(ErrorUnusedCli)
diff --git a/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt new file mode 100644 index 0000000..6d9315e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath-stderr.txt
@@ -0,0 +1,5 @@ +CMake Warning \(unused-cli\): + Manually-specified variables were not used by the project: + + RunCMake_GENERATOR + UNUSED_VARIABLE$
diff --git a/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake new file mode 100644 index 0000000..fc59b1c --- /dev/null +++ b/Tests/RunCMake/CMakePresets/WarningAbsInstallPath.cmake
@@ -0,0 +1,6 @@ +cmake_diagnostic(GET CMD_INSTALL_ABSOLUTE_DESTINATION action) +if(NOT "${action}" STREQUAL WARN) + message(SEND_ERROR + "wrong action for diagnostic CMD_INSTALL_ABSOLUTE_DESTINATION" + " (expected 'WARN', actual '${action}')") +endif()
diff --git a/Tests/RunCMake/CMakePresets/Warnings12.json.in b/Tests/RunCMake/CMakePresets/Warnings12.json.in index 650f111..701e5f4 100644 --- a/Tests/RunCMake/CMakePresets/Warnings12.json.in +++ b/Tests/RunCMake/CMakePresets/Warnings12.json.in
@@ -30,6 +30,20 @@ } }, { + "name": "WarningAbsInstallPath", + "inherits": "NoWarningFlags", + "warnings": { + "installAbsoluteDestination": true + } + }, + { + "name": "ErrorAbsInstallPath", + "inherits": "NoWarningFlags", + "errors": { + "installAbsoluteDestination": true + } + }, + { "name": "ErrorDev", "inherits": "NoWarningFlags", "errors": {
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-OFF-env-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVerify-ON-env-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-cmake-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt +++ b/Tests/RunCMake/CTestCommandLine/FailDrop-TLSVersion-1.1-env-ctest-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/Diagnostics/CacheInit.cmake b/Tests/RunCMake/Diagnostics/CacheInit.cmake index 1af1495..24baac2 100644 --- a/Tests/RunCMake/Diagnostics/CacheInit.cmake +++ b/Tests/RunCMake/Diagnostics/CacheInit.cmake
@@ -1,4 +1,5 @@ include(Assertions.cmake) expect(CMD_AUTHOR IGNORE) +expect(CMD_INSTALL_ABSOLUTE_DESTINATION IGNORE) expect(CMD_DEPRECATED SEND_ERROR)
diff --git a/Tests/RunCMake/Diagnostics/CommandLine3.cmake b/Tests/RunCMake/Diagnostics/CommandLine3.cmake index e11e17d..96f8e1a 100644 --- a/Tests/RunCMake/Diagnostics/CommandLine3.cmake +++ b/Tests/RunCMake/Diagnostics/CommandLine3.cmake
@@ -1,3 +1,5 @@ include(Assertions.cmake) expect_cached(CMD_UNINITIALIZED IGNORE) + +expect_cached(CMD_INSTALL_ABSOLUTE_DESTINATION WARN)
diff --git a/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake b/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake index 5ffb6fa..5f77f8e 100644 --- a/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake +++ b/Tests/RunCMake/Diagnostics/RunCMakeTest.cmake
@@ -12,4 +12,4 @@ run_cmake_with_options(CommandLine1 -Werror=author -Wdeprecated) run_cmake_with_options(CommandLine1 -Wno-deprecated -Werror=author) run_cmake_with_options(CommandLine2 -Werror=author -Wno-deprecated) -run_cmake_with_options(CommandLine3 -Wno-error=uninitialized) +run_cmake_with_options(CommandLine3 -Wno-error=uninitialized -Winstall-absolute-destination)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake new file mode 100644 index 0000000..90038ea --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep-check.cmake
@@ -0,0 +1,14 @@ +# The link edge for a Swift library must implicitly depend on the +# .swiftmodule so the emit-module edge runs even when no other target +# in the build depends on it. +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +if(NOT build_ninja MATCHES "build [^\n]*(libL\\.a|L\\.lib)[^\n]*:.*\\|[^\n]*L\\.swiftmodule") + string(APPEND RunCMake_TEST_FAILED + "Link edge for L does not depend on L.swiftmodule.\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake new file mode 100644 index 0000000..7ef69cf --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDep.cmake
@@ -0,0 +1,12 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# A standalone library with no dependent Swift targets. The link edge +# must still depend on the .swiftmodule so the emit-module edge runs. +add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake new file mode 100644 index 0000000..2aca41d --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath-check.cmake
@@ -0,0 +1,14 @@ +# Same as EmitModuleSeparatelyLinkDep but with a custom module directory. +# The link edge must depend on the actual emit-module output path, not a +# default that doesn't match the target's configuration. +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +if(NOT build_ninja MATCHES "build [^\n]*(libL\\.a|L\\.lib)[^\n]*:.*\\|[^\n]*custom(/|\\\\)[^\n]*L\\.swiftmodule") + string(APPEND RunCMake_TEST_FAILED + "Link edge for L does not depend on custom/.../L.swiftmodule.\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake new file mode 100644 index 0000000..fbb3ac8 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyLinkDepCustomPath.cmake
@@ -0,0 +1,15 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# A standalone library with a custom module output path (like swiftCore +# in the Swift stdlib). The link edge must depend on the actual +# emit-module output, not GetSwiftModulePath(). +add_library(L STATIC L.swift) +set_target_properties(L PROPERTIES + Swift_MODULE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/custom)
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake new file mode 100644 index 0000000..b42cfd1 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering-check.cmake
@@ -0,0 +1,28 @@ +# The Swift object compile edge must order-only depend on the .swiftmodule +# produced by the separate emit-module edge. Both edges share the same +# -output-file-map; running them concurrently corrupts the module-level +# swift-dependencies file referenced by that map. +if(RunCMake_GENERATOR_IS_MULTI_CONFIG) + set(path "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/impl-Debug.ninja") +else() + set(path "${RunCMake_TEST_BINARY_DIR}/build.ninja") +endif() +file(READ "${path}" build_ninja) + +# Find the object compile edge for L and confirm L.swiftmodule appears +# after the order-only `||` separator. The edge ends at the next `build ` +# at the start of a line (or end of file). +string(REGEX MATCH + "build [^\n]*L\\.swift\\.o[^\n]*:[^\n]*(\n [^\n]+)*" + obj_edge "${build_ninja}") + +if(NOT obj_edge) + string(APPEND RunCMake_TEST_FAILED + "Could not find object compile edge for L.swift.\n") + return() +endif() + +if(NOT obj_edge MATCHES "\\|\\|[^\n]*L\\.swiftmodule") + string(APPEND RunCMake_TEST_FAILED + "Object compile edge for L.swift is missing an order-only dependency on L.swiftmodule.\nEdge:\n${obj_edge}\n") +endif()
diff --git a/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake new file mode 100644 index 0000000..6264453 --- /dev/null +++ b/Tests/RunCMake/Swift/EmitModuleSeparatelyOrdering.cmake
@@ -0,0 +1,14 @@ +cmake_policy(SET CMP0157 NEW) +cmake_policy(SET CMP0215 NEW) + +if(NOT CMAKE_GENERATOR MATCHES "Ninja") + message(SEND_ERROR "this test must use a Ninja generator, found ${CMAKE_GENERATOR}") +endif() + +enable_language(Swift) + +# The compile and emit-module edges share the same -output-file-map. +# Within the target, the object compile edge must order-only depend on the +# .swiftmodule so the two edges do not run concurrently and corrupt the +# shared module-level swift-dependencies entry. +add_library(L STATIC L.swift)
diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake index 5b63f43..a56d6bf 100644 --- a/Tests/RunCMake/Swift/RunCMakeTest.cmake +++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake
@@ -171,6 +171,14 @@ endblock() block() + run_cmake(EmitModuleSeparatelyLinkDep) + endblock() + + block() + run_cmake(EmitModuleSeparatelyLinkDepCustomPath) + endblock() + + block() set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/EmitModuleSeparatelyExistingModulePath-build) run_cmake(EmitModuleSeparatelyExistingModulePath) set(RunCMake_TEST_NO_CLEAN 1) @@ -179,6 +187,10 @@ endblock() block() + run_cmake(EmitModuleSeparatelyOrdering) + endblock() + + block() if(CMAKE_SYSTEM_NAME MATCHES Windows) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ImportLibraryFlags-build) run_cmake(ImportLibraryFlags)
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt index a42e3f4..f6dcbc8 100644 --- a/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt +++ b/Tests/RunCMake/ctest_submit/CDashSubmitHeaders-stderr.txt
@@ -1 +1 @@ - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt index 093eefb..cb14372 100644 --- a/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt +++ b/Tests/RunCMake/ctest_submit/CDashSubmitQuiet-stderr.txt
@@ -1,3 +1,3 @@ *Error when uploading file: .*/Configure\.xml - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) *Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt b/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt index a42e3f4..f6dcbc8 100644 --- a/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt +++ b/Tests/RunCMake/ctest_submit/CDashSubmitVerbose-stderr.txt
@@ -1 +1 @@ - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt index a42e3f4..f6dcbc8 100644 --- a/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FILESNoBuildId-stderr.txt
@@ -1 +1 @@ - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*)
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-cmake-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-env-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-OFF-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-cmake-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-env-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVerify-ON-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-cmake-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-env-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-TLSVersion-1.1-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt index 9c3402e..d1a06c4 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-http-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt b/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt index 24d412d..085e90a 100644 --- a/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt +++ b/Tests/RunCMake/ctest_submit/FailDrop-https-stderr.txt
@@ -1,2 +1,2 @@ -Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* +Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?|The requested URL returned error:|Protocol "https" (not supported or disabled|not supported|disabled)|.* was built with SSL disabled).* Problems when submitting via HTTP
diff --git a/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt b/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt index 2c5f900..8210ee3 100644 --- a/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt +++ b/Tests/RunCMake/ctest_submit/PARTSDone-stderr.txt
@@ -1,3 +1,3 @@ *Error when uploading file: .*/Done\.xml - *Error message was: ([Cc]ould *n.t resolve host:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) + *Error message was: ([Cc]ould *n.t resolve( host)?:? '?badhostname\.invalid'?.*|The requested URL returned error:.*) *Problems when submitting via HTTP
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt index f6391ad..1f4da93 100644 --- a/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt +++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@ find_file command specified for VALIDATOR is not a function: check\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_file\): + find_file command specified for VALIDATOR is not a function: check2\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake index ec0ce9a..861fbf5 100644 --- a/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake +++ b/Tests/RunCMake/find_file/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@ endmacro() find_file(result NAMES input.txt VALIDATOR check) + +add_subdirectory(subdir) +find_file(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_file/subdir/CMakeLists.txt b/Tests/RunCMake/find_file/subdir/CMakeLists.txt new file mode 100644 index 0000000..1edb776 --- /dev/null +++ b/Tests/RunCMake/find_file/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@ + +macro(CHECK2 result path) +endmacro()
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt index 8f7f8c4..c220085 100644 --- a/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt +++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@ find_library command specified for VALIDATOR is not a function: check\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_library\): + find_library command specified for VALIDATOR is not a function: check2\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake index fa90d72..f652828 100644 --- a/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake +++ b/Tests/RunCMake/find_library/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@ endmacro() find_library(result NAMES input.txt VALIDATOR check) + +add_subdirectory(subdir) +find_library(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_library/subdir/CMakeLists.txt b/Tests/RunCMake/find_library/subdir/CMakeLists.txt new file mode 100644 index 0000000..1edb776 --- /dev/null +++ b/Tests/RunCMake/find_library/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@ + +macro(CHECK2 result path) +endmacro()
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt index 9f0e3e8..bff9f35 100644 --- a/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt +++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@ find_path command specified for VALIDATOR is not a function: check\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_path\): + find_path command specified for VALIDATOR is not a function: check2\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake index 62b44ed..f1faded 100644 --- a/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake +++ b/Tests/RunCMake/find_path/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@ endmacro() find_path(result NAMES input.txt VALIDATOR check) + +add_subdirectory(subdir) +find_path(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_path/subdir/CMakeLists.txt b/Tests/RunCMake/find_path/subdir/CMakeLists.txt new file mode 100644 index 0000000..1edb776 --- /dev/null +++ b/Tests/RunCMake/find_path/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@ + +macro(CHECK2 result path) +endmacro()
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt index 949f29d..567e577 100644 --- a/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt +++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro-stderr.txt
@@ -2,3 +2,9 @@ find_program command specified for VALIDATOR is not a function: check\. Call Stack \(most recent call first\): CMakeLists\.txt:[0-9]+ \(include\) + + +CMake Error at VALIDATOR-specify-macro\.cmake:[0-9]+ \(find_program\): + find_program command specified for VALIDATOR is not a function: check2\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake index 43bb9ec..3e5e579 100644 --- a/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake +++ b/Tests/RunCMake/find_program/VALIDATOR-specify-macro.cmake
@@ -3,3 +3,6 @@ endmacro() find_program(result NAMES input.txt VALIDATOR check) + +add_subdirectory(subdir) +find_program(result NAMES input.txt VALIDATOR check2)
diff --git a/Tests/RunCMake/find_program/subdir/CMakeLists.txt b/Tests/RunCMake/find_program/subdir/CMakeLists.txt new file mode 100644 index 0000000..1edb776 --- /dev/null +++ b/Tests/RunCMake/find_program/subdir/CMakeLists.txt
@@ -0,0 +1,3 @@ + +macro(CHECK2 result path) +endmacro()
diff --git a/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-result.txt b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-result.txt
@@ -0,0 +1 @@ +1
diff --git a/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt new file mode 100644 index 0000000..d742940 --- /dev/null +++ b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error \(install-absolute-destination\) at DIRECTORY-AbsoluteDest-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake new file mode 100644 index 0000000..a76ddb4 --- /dev/null +++ b/Tests/RunCMake/install/DIRECTORY-AbsoluteDest-error.cmake
@@ -0,0 +1 @@ +install(DIRECTORY DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-error-result.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-error-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-error-result.txt
@@ -0,0 +1 @@ +1
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt new file mode 100644 index 0000000..689d48b --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error \(install-absolute-destination\) at FILES-AbsoluteDest-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake b/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake new file mode 100644 index 0000000..28cb56b --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-error.cmake
@@ -0,0 +1 @@ +install(FILES empty.c DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..f320131 --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,2 @@ +CMake Warning \(install-absolute-destination\) at FILES-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..28cb56b --- /dev/null +++ b/Tests/RunCMake/install/FILES-AbsoluteDest-warn.cmake
@@ -0,0 +1 @@ +install(FILES empty.c DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index ff8e812..5e40e88 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -82,6 +82,14 @@ run_cmake(FILES-DESTINATION-bad) run_cmake(FILES-RENAME-bad) run_cmake(TARGETS-DESTINATION-bad) +run_cmake_with_options(DIRECTORY-AbsoluteDest-error -Werror=install-absolute-destination) +run_cmake_with_options(FILES-AbsoluteDest-error -Werror=install-absolute-destination) +run_cmake_with_options(FILES-AbsoluteDest-warn -Winstall-absolute-destination) +run_cmake_with_options(TARGETS-AbsoluteDest-error -Werror=install-absolute-destination) +run_cmake_with_options(TARGETS-AbsoluteDest-warn -Winstall-absolute-destination) +run_cmake_with_options(TARGETS-AbsoluteDest-archive-error -Werror=install-absolute-destination) +run_cmake_with_options(TARGETS-AbsoluteDest-library-error -Werror=install-absolute-destination) +run_cmake_with_options(TARGETS-AbsoluteDest-runtime-error -Werror=install-absolute-destination) run_cmake(EXPORT-Component) run_cmake(EXPORT-FindDependencyExportGate) run_cmake(EXPORT-OldIFace)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-result.txt
@@ -0,0 +1 @@ +1
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt new file mode 100644 index 0000000..e091ab0 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-archive-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path \(/absolute/archive\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake new file mode 100644 index 0000000..3ecc7e3 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-archive-error.cmake
@@ -0,0 +1,3 @@ +enable_language(C) +add_library(mylib STATIC empty.c) +install(TARGETS mylib ARCHIVE DESTINATION /absolute/archive)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-result.txt
@@ -0,0 +1 @@ +1
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt new file mode 100644 index 0000000..22c0d98 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake new file mode 100644 index 0000000..b16af5c --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-error.cmake
@@ -0,0 +1,12 @@ +enable_language(C) +add_library(mylib STATIC empty.c) + +# FIXME(#27770): This diagnostic is issued at generate time. Currently, the +# install generator doesn't capture the immediate diagnostic state, only the +# top-most state of the current subdirectory, which we cannot affect (because +# this test was include()d. When we fix that, we should test by changing the +# diagnostic action here rather than by -W... in the test CLI arguments. +# cmake_diagnostic(SET CMD_INSTALL_ABSOLUTE_DESTINATION SEND_ERROR) + +cmake_diagnostic(PROMOTE CMD_INSTALL_ABSOLUTE_DESTINATION WARN) +install(TARGETS mylib DESTINATION /absolute/path)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-result.txt
@@ -0,0 +1 @@ +1
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt new file mode 100644 index 0000000..368434d --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-library-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path \(/absolute/lib\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake new file mode 100644 index 0000000..b5be8fd --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-library-error.cmake
@@ -0,0 +1,3 @@ +enable_language(C) +add_library(mylib MODULE empty.c) +install(TARGETS mylib LIBRARY DESTINATION /absolute/lib)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-result.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-result.txt
@@ -0,0 +1 @@ +1
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt new file mode 100644 index 0000000..279fb88 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error-stderr.txt
@@ -0,0 +1,2 @@ +CMake Error \(install-absolute-destination\) at TARGETS-AbsoluteDest-runtime-error\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path \(/absolute/bin\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake new file mode 100644 index 0000000..638f7fb --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-runtime-error.cmake
@@ -0,0 +1,3 @@ +enable_language(C) +add_executable(myexe empty.c) +install(TARGETS myexe RUNTIME DESTINATION /absolute/bin)
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt new file mode 100644 index 0000000..730a8ed --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn-stderr.txt
@@ -0,0 +1,2 @@ +CMake Warning \(install-absolute-destination\) at TARGETS-AbsoluteDest-warn\.cmake:[0-9]+ \(install\): + INSTALL command given absolute DESTINATION path \(/absolute/path\)\.
diff --git a/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake new file mode 100644 index 0000000..e2ca4be --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-AbsoluteDest-warn.cmake
@@ -0,0 +1,3 @@ +enable_language(C) +add_library(mylib STATIC empty.c) +install(TARGETS mylib DESTINATION /absolute/path)
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash index 3964f5a..213b297 100755 --- a/Utilities/Scripts/update-curl.bash +++ b/Utilities/Scripts/update-curl.bash
@@ -8,7 +8,7 @@ readonly ownership="Curl Upstream <curl-library@lists.haxx.se>" readonly subtree="Utilities/cmcurl" readonly repo="https://github.com/curl/curl.git" -readonly tag="curl-8_19_0" +readonly tag="curl-8_20_0" readonly shortlog=false readonly exact_tree_match=false readonly paths="
diff --git a/Utilities/cmcurl/CMake/FindBrotli.cmake b/Utilities/cmcurl/CMake/FindBrotli.cmake index b5437e7..bd3363c 100644 --- a/Utilities/cmcurl/CMake/FindBrotli.cmake +++ b/Utilities/cmcurl/CMake/FindBrotli.cmake
@@ -84,10 +84,6 @@ endif() if(BROTLI_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_brotli_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::brotli) add_library(CURL::brotli INTERFACE IMPORTED) set_target_properties(CURL::brotli PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindCares.cmake b/Utilities/cmcurl/CMake/FindCares.cmake index 3c05c3b..26efdf8 100644 --- a/Utilities/cmcurl/CMake/FindCares.cmake +++ b/Utilities/cmcurl/CMake/FindCares.cmake
@@ -37,11 +37,15 @@ set(_cares_pc_requires "libcares") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED CARES_INCLUDE_DIR AND +if(NOT DEFINED CARES_INCLUDE_DIR AND NOT DEFINED CARES_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_cares ${_cares_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_cares ${_cares_pc_requires}) + endif() + if(NOT _cares_FOUND AND CURL_USE_CMAKECONFIG) + find_package(c-ares CONFIG QUIET) + endif() endif() if(_cares_FOUND) @@ -55,6 +59,16 @@ set(_cares_LIBRARIES "${_cares_STATIC_LIBRARIES}") endif() message(STATUS "Found Cares (via pkg-config): ${_cares_INCLUDE_DIRS} (found version \"${CARES_VERSION}\")") +elseif(c-ares_CONFIG) + set(Cares_FOUND TRUE) + set(CARES_FOUND TRUE) + set(CARES_VERSION ${c-ares_VERSION}) + if(CARES_USE_STATIC_LIBS) + set(_cares_LIBRARIES c-ares::cares_static) + else() + set(_cares_LIBRARIES c-ares::cares) + endif() + message(STATUS "Found Cares (via CMake Config): ${c-ares_CONFIG} (found version \"${CARES_VERSION}\")") else() find_path(CARES_INCLUDE_DIR NAMES "ares.h") if(CARES_USE_STATIC_LIBS) @@ -106,10 +120,6 @@ list(APPEND _cares_LIBRARIES "iphlpapi") # for if_indextoname and others endif() - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_cares_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::cares) add_library(CURL::cares INTERFACE IMPORTED) set_target_properties(CURL::cares PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindGSS.cmake b/Utilities/cmcurl/CMake/FindGSS.cmake index e36f75b..9237fb3 100644 --- a/Utilities/cmcurl/CMake/FindGSS.cmake +++ b/Utilities/cmcurl/CMake/FindGSS.cmake
@@ -258,10 +258,6 @@ ) if(GSS_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_gss_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::gss) add_library(CURL::gss INTERFACE IMPORTED) set_target_properties(CURL::gss PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindGnuTLS.cmake b/Utilities/cmcurl/CMake/FindGnuTLS.cmake index fff57b2..7f5b227 100644 --- a/Utilities/cmcurl/CMake/FindGnuTLS.cmake +++ b/Utilities/cmcurl/CMake/FindGnuTLS.cmake
@@ -80,10 +80,6 @@ endif() if(GNUTLS_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_gnutls_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::gnutls) add_library(CURL::gnutls INTERFACE IMPORTED) set_target_properties(CURL::gnutls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLDAP.cmake b/Utilities/cmcurl/CMake/FindLDAP.cmake index 2f5cc71..8902b23 100644 --- a/Utilities/cmcurl/CMake/FindLDAP.cmake +++ b/Utilities/cmcurl/CMake/FindLDAP.cmake
@@ -103,10 +103,6 @@ endif() if(LDAP_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_ldap_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::ldap) add_library(CURL::ldap INTERFACE IMPORTED) set_target_properties(CURL::ldap PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibbacktrace.cmake b/Utilities/cmcurl/CMake/FindLibbacktrace.cmake index c6f7b70..59da7fd 100644 --- a/Utilities/cmcurl/CMake/FindLibbacktrace.cmake +++ b/Utilities/cmcurl/CMake/FindLibbacktrace.cmake
@@ -47,10 +47,6 @@ set(_libbacktrace_INCLUDE_DIRS ${LIBBACKTRACE_INCLUDE_DIR}) set(_libbacktrace_LIBRARIES ${LIBBACKTRACE_LIBRARY}) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libbacktrace_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libbacktrace) add_library(CURL::libbacktrace INTERFACE IMPORTED) set_target_properties(CURL::libbacktrace PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibgsasl.cmake b/Utilities/cmcurl/CMake/FindLibgsasl.cmake index 5ddf957..7fbaa71 100644 --- a/Utilities/cmcurl/CMake/FindLibgsasl.cmake +++ b/Utilities/cmcurl/CMake/FindLibgsasl.cmake
@@ -79,10 +79,6 @@ endif() if(LIBGSASL_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libgsasl_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libgsasl) add_library(CURL::libgsasl INTERFACE IMPORTED) set_target_properties(CURL::libgsasl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibidn2.cmake b/Utilities/cmcurl/CMake/FindLibidn2.cmake index 336a7f7..dc78734 100644 --- a/Utilities/cmcurl/CMake/FindLibidn2.cmake +++ b/Utilities/cmcurl/CMake/FindLibidn2.cmake
@@ -80,10 +80,6 @@ endif() if(LIBIDN2_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libidn2_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libidn2) add_library(CURL::libidn2 INTERFACE IMPORTED) set_target_properties(CURL::libidn2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibpsl.cmake b/Utilities/cmcurl/CMake/FindLibpsl.cmake index 9b1a0cd..2287e67 100644 --- a/Utilities/cmcurl/CMake/FindLibpsl.cmake +++ b/Utilities/cmcurl/CMake/FindLibpsl.cmake
@@ -80,10 +80,6 @@ endif() if(LIBPSL_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libpsl_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libpsl) add_library(CURL::libpsl INTERFACE IMPORTED) set_target_properties(CURL::libpsl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibrtmp.cmake b/Utilities/cmcurl/CMake/FindLibrtmp.cmake deleted file mode 100644 index 853ba63..0000000 --- a/Utilities/cmcurl/CMake/FindLibrtmp.cmake +++ /dev/null
@@ -1,116 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. -# -# This software is licensed as described in the file COPYING, which -# you should have received as part of this distribution. The terms -# are also available at https://curl.se/docs/copyright.html. -# -# You may opt to use, copy, modify, merge, publish, distribute and/or sell -# copies of the Software, and permit persons to whom the Software is -# furnished to do so, under the terms of the COPYING file. -# -# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY -# KIND, either express or implied. -# -# SPDX-License-Identifier: curl -# -########################################################################### -# Find the librtmp library -# -# Input variables: -# -# - `LIBRTMP_INCLUDE_DIR`: Absolute path to librtmp include directory. -# - `LIBRTMP_LIBRARY`: Absolute path to `librtmp` library. -# -# Defines: -# -# - `LIBRTMP_FOUND`: System has librtmp. -# - `LIBRTMP_VERSION`: Version of librtmp. -# - `CURL::librtmp`: librtmp library target. - -set(_librtmp_pc_requires "librtmp") - -if(CURL_USE_PKGCONFIG AND - NOT DEFINED LIBRTMP_INCLUDE_DIR AND - NOT DEFINED LIBRTMP_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_librtmp ${_librtmp_pc_requires}) -endif() - -if(_librtmp_FOUND AND _librtmp_INCLUDE_DIRS) - set(Librtmp_FOUND TRUE) - set(LIBRTMP_FOUND TRUE) - set(LIBRTMP_VERSION ${_librtmp_VERSION}) - message(STATUS "Found Librtmp (via pkg-config): ${_librtmp_INCLUDE_DIRS} (found version \"${LIBRTMP_VERSION}\")") -else() - find_path(LIBRTMP_INCLUDE_DIR NAMES "librtmp/rtmp.h") - find_library(LIBRTMP_LIBRARY NAMES "rtmp") - - unset(LIBRTMP_VERSION CACHE) - if(LIBRTMP_INCLUDE_DIR AND EXISTS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h") - set(_version_regex "#[\t ]*define[\t ]+RTMP_LIB_VERSION[\t ]+0x([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F]).*") - file(STRINGS "${LIBRTMP_INCLUDE_DIR}/librtmp/rtmp.h" _version_str REGEX "${_version_regex}") - string(REGEX REPLACE "${_version_regex}" "\\1" _version_str1 "${_version_str}") - string(REGEX REPLACE "${_version_regex}" "\\2" _version_str2 "${_version_str}") - if(CMAKE_VERSION VERSION_LESS 3.13) - # No support for hex version numbers, just strip leading zeroes - string(REGEX REPLACE "^0" "" _version_str1 "${_version_str1}") - string(REGEX REPLACE "^0" "" _version_str2 "${_version_str2}") - else() - math(EXPR _version_str1 "0x${_version_str1}" OUTPUT_FORMAT DECIMAL) - math(EXPR _version_str2 "0x${_version_str2}" OUTPUT_FORMAT DECIMAL) - endif() - set(LIBRTMP_VERSION "${_version_str1}.${_version_str2}") - unset(_version_regex) - unset(_version_str1) - unset(_version_str2) - endif() - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Librtmp - REQUIRED_VARS - LIBRTMP_INCLUDE_DIR - LIBRTMP_LIBRARY - VERSION_VAR - LIBRTMP_VERSION - ) - - if(LIBRTMP_FOUND) - set(_librtmp_INCLUDE_DIRS ${LIBRTMP_INCLUDE_DIR}) - set(_librtmp_LIBRARIES ${LIBRTMP_LIBRARY}) - endif() - - mark_as_advanced(LIBRTMP_INCLUDE_DIR LIBRTMP_LIBRARY) - - # Necessary when linking a static librtmp - find_package(OpenSSL) - if(OpenSSL_FOUND) - list(APPEND _librtmp_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) - endif() -endif() - -if(LIBRTMP_FOUND) - if(WIN32) - list(APPEND _librtmp_LIBRARIES "winmm") - endif() - - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_librtmp_LIBRARY_DIRS}) - endif() - - if(NOT TARGET CURL::librtmp) - add_library(CURL::librtmp INTERFACE IMPORTED) - set_target_properties(CURL::librtmp PROPERTIES - INTERFACE_LIBCURL_PC_MODULES "${_librtmp_pc_requires}" - INTERFACE_COMPILE_OPTIONS "${_librtmp_CFLAGS}" - INTERFACE_INCLUDE_DIRECTORIES "${_librtmp_INCLUDE_DIRS}" - INTERFACE_LINK_DIRECTORIES "${_librtmp_LIBRARY_DIRS}" - INTERFACE_LINK_LIBRARIES "${_librtmp_LIBRARIES}") - endif() -endif()
diff --git a/Utilities/cmcurl/CMake/FindLibssh.cmake b/Utilities/cmcurl/CMake/FindLibssh.cmake index 732ab8e..3837d4d 100644 --- a/Utilities/cmcurl/CMake/FindLibssh.cmake +++ b/Utilities/cmcurl/CMake/FindLibssh.cmake
@@ -106,10 +106,6 @@ list(APPEND _libssh_LIBRARIES "iphlpapi") # for if_nametoindex endif() - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libssh_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libssh) add_library(CURL::libssh INTERFACE IMPORTED) set_target_properties(CURL::libssh PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibssh2.cmake b/Utilities/cmcurl/CMake/FindLibssh2.cmake index abed471..22481d6 100644 --- a/Utilities/cmcurl/CMake/FindLibssh2.cmake +++ b/Utilities/cmcurl/CMake/FindLibssh2.cmake
@@ -37,11 +37,15 @@ set(_libssh2_pc_requires "libssh2") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED LIBSSH2_INCLUDE_DIR AND +if(NOT DEFINED LIBSSH2_INCLUDE_DIR AND NOT DEFINED LIBSSH2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_libssh2 ${_libssh2_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_libssh2 ${_libssh2_pc_requires}) + endif() + if(NOT _libssh2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(libssh2 CONFIG QUIET) + endif() endif() if(_libssh2_FOUND AND _libssh2_INCLUDE_DIRS) @@ -55,6 +59,16 @@ set(_libssh2_LIBRARIES "${_libssh2_STATIC_LIBRARIES}") endif() message(STATUS "Found Libssh2 (via pkg-config): ${_libssh2_INCLUDE_DIRS} (found version \"${LIBSSH2_VERSION}\")") +elseif(libssh2_CONFIG) + set(Libssh2_FOUND TRUE) + set(LIBSSH2_FOUND TRUE) + set(LIBSSH2_VERSION ${libssh2_VERSION}) + if(LIBSSH2_USE_STATIC_LIBS) + set(_libssh2_LIBRARIES libssh2::libssh2_static) + else() + set(_libssh2_LIBRARIES libssh2::libssh2) + endif() + message(STATUS "Found Libssh2 (via CMake Config): ${libssh2_CONFIG} (found version \"${LIBSSH2_VERSION}\")") else() find_path(LIBSSH2_INCLUDE_DIR NAMES "libssh2.h") if(LIBSSH2_USE_STATIC_LIBS) @@ -91,10 +105,6 @@ endif() if(LIBSSH2_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libssh2_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libssh2) add_library(CURL::libssh2 INTERFACE IMPORTED) set_target_properties(CURL::libssh2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindLibuv.cmake b/Utilities/cmcurl/CMake/FindLibuv.cmake index f9d614b..47d043c 100644 --- a/Utilities/cmcurl/CMake/FindLibuv.cmake +++ b/Utilities/cmcurl/CMake/FindLibuv.cmake
@@ -90,10 +90,6 @@ endif() if(LIBUV_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_libuv_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::libuv) add_library(CURL::libuv INTERFACE IMPORTED) set_target_properties(CURL::libuv PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindMbedTLS.cmake b/Utilities/cmcurl/CMake/FindMbedTLS.cmake index 8b67ce9..21a5f4a 100644 --- a/Utilities/cmcurl/CMake/FindMbedTLS.cmake +++ b/Utilities/cmcurl/CMake/FindMbedTLS.cmake
@@ -45,13 +45,17 @@ set(_mbedtls_pc_requires "mbedtls" "mbedx509" "mbedcrypto") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED MBEDTLS_INCLUDE_DIR AND +if(NOT DEFINED MBEDTLS_INCLUDE_DIR AND NOT DEFINED MBEDTLS_LIBRARY AND NOT DEFINED MBEDX509_LIBRARY AND NOT DEFINED MBEDCRYPTO_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_mbedtls ${_mbedtls_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_mbedtls ${_mbedtls_pc_requires}) + endif() + if(NOT _mbedtls_FOUND AND CURL_USE_CMAKECONFIG) + find_package(MbedTLS CONFIG QUIET) + endif() endif() if(_mbedtls_FOUND) @@ -65,6 +69,17 @@ set(_mbedtls_LIBRARIES "${_mbedtls_STATIC_LIBRARIES}") endif() message(STATUS "Found MbedTLS (via pkg-config): ${_mbedtls_INCLUDE_DIRS} (found version \"${MBEDTLS_VERSION}\")") +elseif(MbedTLS_CONFIG) + set(MbedTLS_FOUND TRUE) + set(MBEDTLS_FOUND TRUE) + set(MBEDTLS_VERSION ${MbedTLS_VERSION}) + if(MBEDTLS_VERSION GREATER_EQUAL 4.0.0) + set(_mbedtls_LIBRARIES MbedTLS::tfpsacrypto) + else() + set(_mbedtls_LIBRARIES MbedTLS::mbedcrypto) + endif() + list(APPEND _mbedtls_LIBRARIES MbedTLS::mbedx509 MbedTLS::mbedtls) + message(STATUS "Found MbedTLS (via CMake Config): ${MbedTLS_CONFIG} (found version \"${MBEDTLS_VERSION}\")") else() set(_mbedtls_pc_requires "") # Depend on pkg-config only when found via pkg-config @@ -110,10 +125,6 @@ endif() if(MBEDTLS_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_mbedtls_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::mbedtls) add_library(CURL::mbedtls INTERFACE IMPORTED) set_target_properties(CURL::mbedtls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake index b2a8c97..f93113f 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP2.cmake
@@ -37,11 +37,15 @@ set(_nghttp2_pc_requires "libnghttp2") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGHTTP2_INCLUDE_DIR AND +if(NOT DEFINED NGHTTP2_INCLUDE_DIR AND NOT DEFINED NGHTTP2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_nghttp2 ${_nghttp2_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_nghttp2 ${_nghttp2_pc_requires}) + endif() + if(NOT _nghttp2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(nghttp2 CONFIG QUIET) + endif() endif() if(_nghttp2_FOUND) @@ -54,6 +58,15 @@ set(_nghttp2_LIBRARIES "${_nghttp2_STATIC_LIBRARIES}") endif() message(STATUS "Found NGHTTP2 (via pkg-config): ${_nghttp2_INCLUDE_DIRS} (found version \"${NGHTTP2_VERSION}\")") +elseif(nghttp2_CONFIG) + set(NGHTTP2_FOUND TRUE) + set(NGHTTP2_VERSION ${nghttp2_VERSION}) + if(NGHTTP2_USE_STATIC_LIBS) + set(_nghttp2_LIBRARIES nghttp2::nghttp2_static) + else() + set(_nghttp2_LIBRARIES nghttp2::nghttp2) + endif() + message(STATUS "Found NGHTTP2 (via CMake Config): ${nghttp2_CONFIG} (found version \"${NGHTTP2_VERSION}\")") else() find_path(NGHTTP2_INCLUDE_DIR NAMES "nghttp2/nghttp2.h") if(NGHTTP2_USE_STATIC_LIBS) @@ -91,10 +104,6 @@ endif() if(NGHTTP2_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_nghttp2_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::nghttp2) add_library(CURL::nghttp2 INTERFACE IMPORTED) set_target_properties(CURL::nghttp2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake index 57550bf..427c139 100644 --- a/Utilities/cmcurl/CMake/FindNGHTTP3.cmake +++ b/Utilities/cmcurl/CMake/FindNGHTTP3.cmake
@@ -37,11 +37,15 @@ set(_nghttp3_pc_requires "libnghttp3") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGHTTP3_INCLUDE_DIR AND +if(NOT DEFINED NGHTTP3_INCLUDE_DIR AND NOT DEFINED NGHTTP3_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_nghttp3 ${_nghttp3_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_nghttp3 ${_nghttp3_pc_requires}) + endif() + if(NOT _nghttp3_FOUND AND CURL_USE_CMAKECONFIG) + find_package(nghttp3 CONFIG QUIET) + endif() endif() if(_nghttp3_FOUND) @@ -54,6 +58,15 @@ set(_nghttp3_LIBRARIES "${_nghttp3_STATIC_LIBRARIES}") endif() message(STATUS "Found NGHTTP3 (via pkg-config): ${_nghttp3_INCLUDE_DIRS} (found version \"${NGHTTP3_VERSION}\")") +elseif(nghttp3_CONFIG) + set(NGHTTP3_FOUND TRUE) + set(NGHTTP3_VERSION ${nghttp3_VERSION}) + if(NGHTTP3_USE_STATIC_LIBS) + set(_nghttp3_LIBRARIES nghttp3::nghttp3_static) + else() + set(_nghttp3_LIBRARIES nghttp3::nghttp3) + endif() + message(STATUS "Found NGHTTP3 (via CMake Config): ${nghttp3_CONFIG} (found version \"${NGHTTP3_VERSION}\")") else() find_path(NGHTTP3_INCLUDE_DIR NAMES "nghttp3/nghttp3.h") if(NGHTTP3_USE_STATIC_LIBS) @@ -91,10 +104,6 @@ endif() if(NGHTTP3_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_nghttp3_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::nghttp3) add_library(CURL::nghttp3 INTERFACE IMPORTED) set_target_properties(CURL::nghttp3 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake index 615ee4a..e492916 100644 --- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake +++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
@@ -74,12 +74,22 @@ endif() set(_tried_pkgconfig FALSE) -if(CURL_USE_PKGCONFIG AND - NOT DEFINED NGTCP2_INCLUDE_DIR AND +if(NOT DEFINED NGTCP2_INCLUDE_DIR AND NOT DEFINED NGTCP2_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires}) - set(_tried_pkgconfig TRUE) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires}) + set(_tried_pkgconfig TRUE) + endif() + if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG) + find_package(ngtcp2 CONFIG QUIET) + # Skip using it if the crypto library target is not available + if(ngtcp2_CONFIG AND + NOT TARGET ngtcp2::${_crypto_library_lower}_static AND + NOT TARGET ngtcp2::${_crypto_library_lower}) + unset(ngtcp2_CONFIG) + endif() + endif() endif() if(_ngtcp2_FOUND) @@ -92,6 +102,15 @@ set(_ngtcp2_LIBRARIES "${_ngtcp2_STATIC_LIBRARIES}") endif() message(STATUS "Found NGTCP2 (via pkg-config): ${_ngtcp2_INCLUDE_DIRS} (found version \"${NGTCP2_VERSION}\")") +elseif(ngtcp2_CONFIG) + set(NGTCP2_FOUND TRUE) + set(NGTCP2_VERSION ${ngtcp2_VERSION}) + if(NGTCP2_USE_STATIC_LIBS) + set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2_static ngtcp2::${_crypto_library_lower}_static) + else() + set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2 ngtcp2::${_crypto_library_lower}) + endif() + message(STATUS "Found NGTCP2 (via CMake Config): ${ngtcp2_CONFIG} (found version \"${NGTCP2_VERSION}\")") else() find_path(NGTCP2_INCLUDE_DIR NAMES "ngtcp2/ngtcp2.h") if(NGTCP2_USE_STATIC_LIBS) @@ -155,10 +174,6 @@ endif() if(NGTCP2_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_ngtcp2_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::ngtcp2) add_library(CURL::ngtcp2 INTERFACE IMPORTED) set_target_properties(CURL::ngtcp2 PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindNettle.cmake b/Utilities/cmcurl/CMake/FindNettle.cmake index d22865f..c963180 100644 --- a/Utilities/cmcurl/CMake/FindNettle.cmake +++ b/Utilities/cmcurl/CMake/FindNettle.cmake
@@ -85,10 +85,6 @@ endif() if(NETTLE_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_nettle_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::nettle) add_library(CURL::nettle INTERFACE IMPORTED) set_target_properties(CURL::nettle PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindQuiche.cmake b/Utilities/cmcurl/CMake/FindQuiche.cmake index 22482d7..137679b 100644 --- a/Utilities/cmcurl/CMake/FindQuiche.cmake +++ b/Utilities/cmcurl/CMake/FindQuiche.cmake
@@ -68,10 +68,6 @@ endif() if(QUICHE_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_quiche_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::quiche) add_library(CURL::quiche INTERFACE IMPORTED) set_target_properties(CURL::quiche PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindRustls.cmake b/Utilities/cmcurl/CMake/FindRustls.cmake index 270e99c..1e07565 100644 --- a/Utilities/cmcurl/CMake/FindRustls.cmake +++ b/Utilities/cmcurl/CMake/FindRustls.cmake
@@ -104,10 +104,6 @@ mark_as_advanced(MATH_LIBRARY) endif() - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_rustls_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::rustls) add_library(CURL::rustls INTERFACE IMPORTED) set_target_properties(CURL::rustls PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindWolfSSL.cmake b/Utilities/cmcurl/CMake/FindWolfSSL.cmake index 3a3c05f..964320a 100644 --- a/Utilities/cmcurl/CMake/FindWolfSSL.cmake +++ b/Utilities/cmcurl/CMake/FindWolfSSL.cmake
@@ -45,11 +45,15 @@ set(_wolfssl_pc_requires "wolfssl") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED WOLFSSL_INCLUDE_DIR AND +if(NOT DEFINED WOLFSSL_INCLUDE_DIR AND NOT DEFINED WOLFSSL_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_wolfssl ${_wolfssl_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_wolfssl ${_wolfssl_pc_requires}) + endif() + if(NOT _wolfssl_FOUND AND CURL_USE_CMAKECONFIG) + find_package(wolfssl CONFIG QUIET) + endif() endif() if(_wolfssl_FOUND) @@ -57,6 +61,12 @@ set(WOLFSSL_FOUND TRUE) set(WOLFSSL_VERSION ${_wolfssl_VERSION}) message(STATUS "Found WolfSSL (via pkg-config): ${_wolfssl_INCLUDE_DIRS} (found version \"${WOLFSSL_VERSION}\")") +elseif(wolfssl_CONFIG) + set(WolfSSL_FOUND TRUE) + set(WOLFSSL_FOUND TRUE) + set(WOLFSSL_VERSION ${wolfssl_VERSION}) + set(_wolfssl_LIBRARIES wolfssl::wolfssl) + message(STATUS "Found WolfSSL (via CMake Config): ${wolfssl_CONFIG} (found version \"${WOLFSSL_VERSION}\")") else() find_path(WOLFSSL_INCLUDE_DIR NAMES "wolfssl/ssl.h") find_library(WOLFSSL_LIBRARY NAMES "wolfssl") @@ -113,10 +123,6 @@ mark_as_advanced(MATH_LIBRARY) endif() - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_wolfssl_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::wolfssl) add_library(CURL::wolfssl INTERFACE IMPORTED) set_target_properties(CURL::wolfssl PROPERTIES
diff --git a/Utilities/cmcurl/CMake/FindZstd.cmake b/Utilities/cmcurl/CMake/FindZstd.cmake index baf6148..8dc620a 100644 --- a/Utilities/cmcurl/CMake/FindZstd.cmake +++ b/Utilities/cmcurl/CMake/FindZstd.cmake
@@ -46,11 +46,21 @@ set(_zstd_pc_requires "libzstd") -if(CURL_USE_PKGCONFIG AND - NOT DEFINED ZSTD_INCLUDE_DIR AND +if(NOT DEFINED ZSTD_INCLUDE_DIR AND NOT DEFINED ZSTD_LIBRARY) - find_package(PkgConfig QUIET) - pkg_check_modules(_zstd ${_zstd_pc_requires}) + if(CURL_USE_PKGCONFIG) + find_package(PkgConfig QUIET) + pkg_check_modules(_zstd ${_zstd_pc_requires}) + endif() + if(NOT _zstd_FOUND AND CURL_USE_CMAKECONFIG) + find_package(Zstd CONFIG QUIET) + # Skip using if older than v1.4.5 + if(Zstd_CONFIG AND + NOT TARGET zstd::libzstd_static AND + NOT TARGET zstd::libzstd_shared) + unset(Zstd_CONFIG) + endif() + endif() endif() if(_zstd_FOUND) @@ -64,6 +74,17 @@ set(_zstd_LIBRARIES "${_zstd_STATIC_LIBRARIES}") endif() message(STATUS "Found Zstd (via pkg-config): ${_zstd_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")") +elseif(Zstd_CONFIG) + set(ZSTD_FOUND TRUE) + set(ZSTD_VERSION ${Zstd_VERSION}) + if(ZSTD_USE_STATIC_LIBS) + set(_zstd_LIBRARIES zstd::libzstd_static) + elseif(TARGET zstd::libzstd) + set(_zstd_LIBRARIES zstd::libzstd) # v1.5.6+ + else() + set(_zstd_LIBRARIES zstd::libzstd_shared) + endif() + message(STATUS "Found Zstd (via CMake Config): ${Zstd_CONFIG} (found version \"${ZSTD_VERSION}\")") else() find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h") if(ZSTD_USE_STATIC_LIBS) @@ -110,10 +131,6 @@ endif() if(ZSTD_FOUND) - if(CMAKE_VERSION VERSION_LESS 3.13) - link_directories(${_zstd_LIBRARY_DIRS}) - endif() - if(NOT TARGET CURL::zstd) add_library(CURL::zstd INTERFACE IMPORTED) set_target_properties(CURL::zstd PROPERTIES
diff --git a/Utilities/cmcurl/CMake/Macros.cmake b/Utilities/cmcurl/CMake/Macros.cmake index e182f21..5e26c38 100644 --- a/Utilities/cmcurl/CMake/Macros.cmake +++ b/Utilities/cmcurl/CMake/Macros.cmake
@@ -40,20 +40,12 @@ # Return result in variable: CURL_TEST_OUTPUT macro(curl_internal_test _curl_test) if(NOT DEFINED "${_curl_test}") - string(REPLACE ";" " " _cmake_required_definitions "${CMAKE_REQUIRED_DEFINITIONS}") - set(_curl_test_add_libraries "") - if(CMAKE_REQUIRED_LIBRARIES) - set(_curl_test_add_libraries - "-DLINK_LIBRARIES:STRING=${CMAKE_REQUIRED_LIBRARIES}") - endif() - message(STATUS "Performing Test ${_curl_test}") try_compile(${_curl_test} ${PROJECT_BINARY_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c" - CMAKE_FLAGS - "-DCOMPILE_DEFINITIONS:STRING=-D${_curl_test} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${_cmake_required_definitions}" - "${_curl_test_add_libraries}" + COMPILE_DEFINITIONS "-D${_curl_test}" ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}" OUTPUT_VARIABLE CURL_TEST_OUTPUT) if(${_curl_test}) set(${_curl_test} 1 CACHE INTERNAL "curl test") @@ -252,3 +244,35 @@ add_dependencies(tests-clang-tidy ${_target_clang_tidy}) endif() endfunction() + +# Internal: Recurse into interface targets and collect their libraries +# and library paths. +macro(curl_collect_target_link_options _target) + get_target_property(_val ${_target} INTERFACE_LINK_DIRECTORIES) + if(_val) + list(APPEND _libdirs ${_val}) + endif() + get_target_property(_val ${_target} IMPORTED) + if(_val) + # LOCATION is empty for interface library targets and safe to ignore. + # Explicitly skip this query to avoid CMake v3.18 and older erroring out. + get_target_property(_val ${_target} TYPE) + if(NOT "${_val}" STREQUAL "INTERFACE_LIBRARY") + get_target_property(_val ${_target} LOCATION) + if(_val) + list(APPEND _libs ${_val}) + endif() + endif() + endif() + get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES) + if(_val) + foreach(_lib IN LISTS _val) + if(TARGET "${_lib}") + curl_collect_target_link_options(${_lib}) + else() + list(APPEND _libs ${_lib}) + endif() + endforeach() + endif() + unset(_val) +endmacro()
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake index 650a42d..6619f3a 100644 --- a/Utilities/cmcurl/CMake/OtherTests.cmake +++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -78,7 +78,7 @@ elseif(NOT HAVE_GETADDRINFO) set(HAVE_GETADDRINFO_THREADSAFE FALSE) elseif(APPLE OR - CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "HP-UX" OR CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
diff --git a/Utilities/cmcurl/CMake/PickyWarnings.cmake b/Utilities/cmcurl/CMake/PickyWarnings.cmake index 1b67b0b..aca79ec 100644 --- a/Utilities/cmcurl/CMake/PickyWarnings.cmake +++ b/Utilities/cmcurl/CMake/PickyWarnings.cmake
@@ -60,7 +60,9 @@ endif() if(PICKY_COMPILER) - if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") + # Leave disabled for GCC <4.6, because they lack #pragma features to silence locally. + if((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.6) OR + CMAKE_C_COMPILER_ID MATCHES "Clang") # https://clang.llvm.org/docs/DiagnosticsReference.html # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html @@ -147,7 +149,7 @@ list(APPEND _picky_enable ${_picky_common_old} -Wconditional-uninitialized # clang 3.0 - -Wno-used-but-marked-unused # clang 2.9 # Triggered by typecheck-gcc.h with clang 14+, dependency headers + -Wno-used-but-marked-unused # clang 2.9 # for typecheck-gcc.h with clang 14+, dependency headers -Wshift-sign-overflow # clang 2.9 -Wshorten-64-to-32 # clang 1.0 -Wformat=2 # clang 2.7 gcc 4.8 @@ -161,7 +163,7 @@ if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.1) list(APPEND _picky_enable -Wno-covered-switch-default # clang 3.1 appleclang 3.1 # Annoying to fix or silence - -Wno-disabled-macro-expansion # clang 3.1 appleclang 3.1 # Triggered by curl/curl.h, standard headers + -Wno-disabled-macro-expansion # clang 3.1 appleclang 3.1 # for std headers, and curl/curl.h (rare combos) ) if(MSVC) list(APPEND _picky_enable @@ -256,6 +258,7 @@ list(APPEND _picky_enable -Warray-compare # clang 20.1 gcc 12.0 appleclang 26.4 -Wc++-hidden-decl # clang 21.1 appleclang 26.4 + -Wimplicit-int-enum-cast # clang 21.1 -Wjump-misses-init # clang 21.1 gcc 4.5 appleclang 26.4 -Wno-implicit-void-ptr-cast # clang 21.1 appleclang 26.4 -Wtentative-definition-compat # clang 21.1 appleclang 26.4 @@ -347,7 +350,7 @@ endif() endif() - # + # Assemble list of flags set(_picky_skipped "") foreach(_ccopt IN LISTS _picky_enable) @@ -378,15 +381,6 @@ endforeach() if(CMAKE_C_COMPILER_ID STREQUAL "GNU") - if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5) - # Avoid false positives - list(APPEND _picky "-Wno-shadow") - list(APPEND _picky "-Wno-unreachable-code") - endif() - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6) - # GCC <4.6 do not support #pragma to suppress warnings locally. Disable them globally instead. - list(APPEND _picky "-Wno-overlength-strings") - endif() if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.0 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7) list(APPEND _picky "-Wno-missing-field-initializers") # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750 endif() @@ -436,11 +430,13 @@ endforeach() endif() -if(CMAKE_C_STANDARD STREQUAL 90 AND CMAKE_C_COMPILER_ID STREQUAL "AppleClang") - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2) +if(CMAKE_C_STANDARD STREQUAL 90) + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.0) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2)) list(APPEND _picky "-Wno-c99-extensions") # Avoid: warning: '_Bool' is a C99 extension endif() - if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1) + if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9) OR + (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1)) list(APPEND _picky "-Wno-comma") # Just silly endif() endif()
diff --git a/Utilities/cmcurl/CMake/curl-config.in.cmake b/Utilities/cmcurl/CMake/curl-config.in.cmake index 4e7c29f..3174771 100644 --- a/Utilities/cmcurl/CMake/curl-config.in.cmake +++ b/Utilities/cmcurl/CMake/curl-config.in.cmake
@@ -23,6 +23,8 @@ ########################################################################### @PACKAGE_INIT@ +option(CURL_USE_CMAKECONFIG "Enable detecting @PROJECT_NAME@ dependencies via CMake Config. Default: @CURL_USE_CMAKECONFIG@" + "@CURL_USE_CMAKECONFIG@") option(CURL_USE_PKGCONFIG "Enable pkg-config to detect @PROJECT_NAME@ dependencies. Default: @CURL_USE_PKGCONFIG@" "@CURL_USE_PKGCONFIG@") @@ -33,6 +35,10 @@ include(CMakeFindDependencyMacro) +if("@HAVE_THREADS_POSIX@" OR "@HAVE_THREADS_POSIX_BORINGSSL@") + find_dependency(Threads) # for Threads::Threads +endif() + if("@USE_OPENSSL@") if("@OPENSSL_VERSION_MAJOR@") find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@") @@ -42,8 +48,7 @@ # Define lib duplicate to fixup lib order for GCC binutils ld in static builds if(TARGET OpenSSL::Crypto AND NOT TARGET CURL::OpenSSL_Crypto) add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED) - get_target_property(_curl_libname OpenSSL::Crypto LOCATION) - set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES "${_curl_libname}") + set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) endif() endif() if("@HAVE_LIBZ@") @@ -51,13 +56,12 @@ # Define lib duplicate to fixup lib order for GCC binutils ld in static builds if(TARGET ZLIB::ZLIB AND NOT TARGET CURL::ZLIB) add_library(CURL::ZLIB INTERFACE IMPORTED) - get_target_property(_curl_libname ZLIB::ZLIB LOCATION) - set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES "${_curl_libname}") + set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB) endif() endif() set(_curl_cmake_module_path_save ${CMAKE_MODULE_PATH}) -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_MODULE_PATH}) +list(PREPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) set(_curl_libs "") @@ -93,10 +97,6 @@ find_dependency(Libpsl MODULE) list(APPEND _curl_libs CURL::libpsl) endif() -if("@USE_LIBRTMP@") - find_dependency(Librtmp MODULE) - list(APPEND _curl_libs CURL::librtmp) -endif() if("@USE_LIBSSH@") find_dependency(Libssh MODULE) list(APPEND _curl_libs CURL::libssh) @@ -160,35 +160,9 @@ # Alias for either shared or static library if(NOT TARGET @PROJECT_NAME@::@LIB_NAME@) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.11 AND CMAKE_VERSION VERSION_LESS 3.18) - set_target_properties(@PROJECT_NAME@::@LIB_SELECTED@ PROPERTIES IMPORTED_GLOBAL TRUE) - endif() add_library(@PROJECT_NAME@::@LIB_NAME@ ALIAS @PROJECT_NAME@::@LIB_SELECTED@) endif() -if(TARGET @PROJECT_NAME@::@LIB_STATIC@) - # CMake before CMP0099 (CMake 3.17 2020-03-20) did not propagate libdirs to - # targets. It expected libs to have an absolute filename. As a workaround, - # manually apply dependency libdirs, for CMake consumers without this policy. - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) - cmake_policy(GET CMP0099 _has_CMP0099) # https://cmake.org/cmake/help/latest/policy/CMP0099.html - endif() - if(NOT _has_CMP0099 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND _curl_libs) - set(_curl_libdirs "") - foreach(_curl_lib IN LISTS _curl_libs) - if(TARGET "${_curl_lib}") - get_target_property(_curl_libdir "${_curl_lib}" INTERFACE_LINK_DIRECTORIES) - if(_curl_libdir) - list(APPEND _curl_libdirs "${_curl_libdir}") - endif() - endif() - endforeach() - if(_curl_libdirs) - target_link_directories(@PROJECT_NAME@::@LIB_STATIC@ INTERFACE ${_curl_libdirs}) - endif() - endif() -endif() - # For compatibility with CMake's FindCURL.cmake set(CURL_VERSION_STRING "@CURLVERSION@") set(CURL_LIBRARIES @PROJECT_NAME@::@LIB_NAME@)
diff --git a/Utilities/cmcurl/CMake/unix-cache.cmake b/Utilities/cmcurl/CMake/unix-cache.cmake index f358a47..f027f66 100644 --- a/Utilities/cmcurl/CMake/unix-cache.cmake +++ b/Utilities/cmcurl/CMake/unix-cache.cmake
@@ -90,7 +90,6 @@ set(HAVE_FSETXATTR_5 1) set(HAVE_FSETXATTR_6 0) endif() -set(HAVE_FTRUNCATE 1) set(HAVE_GETADDRINFO 1) if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") set(HAVE_GETADDRINFO_THREADSAFE 0) @@ -199,9 +198,12 @@ CYGWIN) set(HAVE_PIPE2 0) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + BSD OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR - CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") + CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "SunOS") set(HAVE_PIPE2 1) endif() set(HAVE_POLL 1) @@ -232,7 +234,6 @@ set(HAVE_SIGINTERRUPT 1) set(HAVE_SIGNAL 1) set(HAVE_SIGSETJMP 1) -set(HAVE_SNPRINTF 1) set(HAVE_SOCKADDR_IN6_SIN6_ADDR 1) set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1) set(HAVE_SOCKET 1)
diff --git a/Utilities/cmcurl/CMake/win32-cache.cmake b/Utilities/cmcurl/CMake/win32-cache.cmake index 77535a6..bd9bb6e 100644 --- a/Utilities/cmcurl/CMake/win32-cache.cmake +++ b/Utilities/cmcurl/CMake/win32-cache.cmake
@@ -29,7 +29,6 @@ set(HAVE_BASENAME 1) set(HAVE_BOOL_T 1) # = HAVE_STDBOOL_H set(HAVE_DIRENT_H 1) - set(HAVE_FTRUNCATE 1) set(HAVE_GETTIMEOFDAY 1) set(HAVE_LIBGEN_H 1) set(HAVE_OPENDIR 1) @@ -42,7 +41,6 @@ set(HAVE_UTIME_H 1) # wrapper to sys/utime.h else() set(HAVE_DIRENT_H 0) - set(HAVE_FTRUNCATE 0) set(HAVE_GETTIMEOFDAY 0) set(HAVE_LIBGEN_H 0) set(HAVE_OPENDIR 0)
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt index 017c6ea..4566ff2 100644 --- a/Utilities/cmcurl/CMakeLists.txt +++ b/Utilities/cmcurl/CMakeLists.txt
@@ -23,6 +23,9 @@ set(CURL_USE_SCHANNEL OFF) set(CURL_USE_WOLFSSL OFF) set(CURL_BROTLI "OFF" CACHE INTERNAL "Build curl with BROTLI support (AUTO, ON or OFF)") +set(CURL_DEBUG_GLOBAL_MEM OFF) +set(CURL_ENABLE_NTLM OFF) +set(CURL_ENABLE_SMB OFF) set(CURL_DISABLE_ALTSVC ON) set(CURL_DISABLE_AWS OFF) set(CURL_DISABLE_BASIC_AUTH OFF) @@ -53,7 +56,6 @@ set(CURL_DISABLE_MQTT ON) set(CURL_DISABLE_NEGOTIATE_AUTH OFF) set(CURL_DISABLE_NETRC OFF) -set(CURL_DISABLE_NTLM OFF) set(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG OFF) set(CURL_DISABLE_PARSEDATE OFF) set(CURL_DISABLE_POP3 ON CACHE INTERNAL "Disable curl pop3 protocol?") @@ -62,7 +64,6 @@ set(CURL_DISABLE_RTSP ON CACHE INTERNAL "Disable curl rtsp protocol?") set(CURL_DISABLE_SHA512_256 OFF) set(CURL_DISABLE_SHUFFLE_DNS OFF) -set(CURL_DISABLE_SMB OFF) set(CURL_DISABLE_SMTP ON CACHE INTERNAL "Disable curl smtp protocol?") set(CURL_DISABLE_SOCKETPAIR OFF) set(CURL_DISABLE_SRP OFF) @@ -75,6 +76,7 @@ set(CURL_HIDDEN_SYMBOLS OFF CACHE INTERNAL "No curl hidden symbols") set(CURL_LTO OFF CACHE INTERNAL "Turn on compiler Link Time Optimizations") set(CURL_STATIC_CRT OFF CACHE INTERNAL "Set to ON to build libcurl with static CRT on Windows (/MT).") +set(CURL_USE_CMAKECONFIG OFF) set(CURL_WERROR OFF CACHE INTERNAL "Turn compiler warnings into errors") set(CURL_ZLIB "AUTO" CACHE INTERNAL "Build curl with ZLIB support (AUTO, ON or OFF)") set(CURL_ZSTD "OFF" CACHE INTERNAL "Build curl with ZSTD support (AUTO, ON or OFF)") @@ -179,7 +181,7 @@ ########################################################################### if(0) # XXX(cmake): not needed for build within cmake -cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR) +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) message(STATUS "Using CMake version ${CMAKE_VERSION}") # Collect command-line arguments for buildinfo.txt. @@ -203,7 +205,7 @@ endif() endif() # XXX(cmake): end -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH}) +list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake") include(Utilities) include(Macros) include(CMakeDependentOption) @@ -447,7 +449,7 @@ set(PICKY_COMPILER OFF) # Do a best effort and skip passing non-clang warning options to clang-tidy. # This lets through warning options enabled via CURL_WERROR=ON, affecting lib and src. endif() - set(CURL_DISABLE_TYPECHECK ON) # to improve performance and avoid potential interference. + set(CURL_DISABLE_TYPECHECK ON) # to improve performance (1.4x), avoid potential interference and bugprone-macro-parentheses. set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY}") list(APPEND CMAKE_C_CLANG_TIDY "--config-file=${PROJECT_SOURCE_DIR}/.clang-tidy.yml") if(CURL_WERROR) @@ -459,6 +461,18 @@ endif() endif() +option(CURL_GCC_ANALYZER "Enable GCC --analyzer option" OFF) +if(CURL_GCC_ANALYZER AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0) + set(CURL_DISABLE_TYPECHECK ON) # to improve performance (1.1x). + # https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html + set(CURL_ANALYZER_CFLAGS "-fanalyzer") + # disable checks causing false positives only + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-fd-leak" "-Wno-analyzer-fd-use-without-check" "-Wno-analyzer-file-leak") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-infinite-loop") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-malloc-leak") + list(APPEND CURL_ANALYZER_CFLAGS "-Wno-analyzer-out-of-bounds") +endif() + option(CURL_CODE_COVERAGE "Enable code coverage build options" OFF) if(CURL_CODE_COVERAGE) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") @@ -486,18 +500,10 @@ option(CURL_DROP_UNUSED "Drop unused code and data from built binaries" OFF) if(CURL_DROP_UNUSED) if(APPLE) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,-dead_strip") - else() - set_property(DIRECTORY APPEND PROPERTY LINK_FLAGS "-Wl,-dead_strip") - endif() + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,-dead_strip") elseif(MSVC) # Options below are toolchain defaults in Release configurations. # This option does not seem to have an effect with VS2010: - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-OPT:REF") - else() - set_property(DIRECTORY APPEND PROPERTY LINK_FLAGS "-OPT:REF") - endif() + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-OPT:REF") # Optional, but reduces binary size further, with the cost of larger objects/static libraries: list(APPEND CURL_CFLAGS "-Gy") elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") @@ -505,11 +511,7 @@ # To make -Wl,--gc-sections work on Windows: https://sourceware.org/bugzilla/show_bug.cgi?id=11539 set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-fno-asynchronous-unwind-tables") endif() - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,--gc-sections") - else() - set_property(DIRECTORY APPEND PROPERTY LINK_FLAGS "-Wl,--gc-sections") - endif() + set_property(DIRECTORY APPEND PROPERTY LINK_OPTIONS "-Wl,--gc-sections") # Optional, but reduces binary size further, with the cost of larger objects/static libraries: list(APPEND CURL_CFLAGS "-ffunction-sections" "-fdata-sections") endif() @@ -560,6 +562,14 @@ endif() endif() +# Override to force-disable or force-enable the use of CMake Configs. +if(MSVC AND NOT VCPKG_TOOLCHAIN AND NOT CMAKE_CROSSCOMPILING) + set(_curl_use_cmakeconfig_default ON) +else() + set(_curl_use_cmakeconfig_default OFF) +endif() +option(CURL_USE_CMAKECONFIG "Enable detecting dependencies via CMake Config" ${_curl_use_cmakeconfig_default}) + # Override to force-disable or force-enable the use of pkg-config. if((UNIX AND NOT ANDROID AND (NOT APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")) OR VCPKG_TOOLCHAIN OR @@ -647,8 +657,8 @@ mark_as_advanced(CURL_DISABLE_BINDLOCAL) option(CURL_DISABLE_NETRC "Disable netrc parser" OFF) mark_as_advanced(CURL_DISABLE_NETRC) -option(CURL_DISABLE_NTLM "Disable NTLM support" OFF) -mark_as_advanced(CURL_DISABLE_NTLM) +option(CURL_ENABLE_NTLM "Enable NTLM support" OFF) +mark_as_advanced(CURL_ENABLE_NTLM) option(CURL_DISABLE_PARSEDATE "Disable date parsing" OFF) mark_as_advanced(CURL_DISABLE_PARSEDATE) option(CURL_DISABLE_POP3 "Disable POP3" OFF) @@ -665,8 +675,8 @@ mark_as_advanced(CURL_DISABLE_SHA512_256) option(CURL_DISABLE_SHUFFLE_DNS "Disable shuffle DNS feature" OFF) mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS) -option(CURL_DISABLE_SMB "Disable SMB" OFF) -mark_as_advanced(CURL_DISABLE_SMB) +option(CURL_ENABLE_SMB "Enable SMB" OFF) +mark_as_advanced(CURL_ENABLE_SMB) option(CURL_DISABLE_SMTP "Disable SMTP" OFF) mark_as_advanced(CURL_DISABLE_SMTP) option(CURL_DISABLE_SOCKETPAIR "Disable use of socketpair for curl_multi_poll()" OFF) @@ -681,7 +691,13 @@ mark_as_advanced(CURL_DISABLE_TYPECHECK) option(CURL_DISABLE_VERBOSE_STRINGS "Disable verbose strings" OFF) mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS) +option(CURL_DEBUG_GLOBAL_MEM "Debug curl_global_init_mem" OFF) +mark_as_advanced(CURL_DEBUG_GLOBAL_MEM) +if(CURL_DEBUG_GLOBAL_MEM) + # Set it via the command-line to make it apply to the entire directory. + set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DEBUG_GLOBAL_MEM") +endif() if(CURL_DISABLE_TYPECHECK) # Set it via the command-line to make it apply to examples also. set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURL_DISABLE_TYPECHECK") @@ -711,7 +727,6 @@ set(CURL_DISABLE_MQTT ON) set(CURL_DISABLE_POP3 ON) set(CURL_DISABLE_RTSP ON) - set(CURL_DISABLE_SMB ON) set(CURL_DISABLE_SMTP ON) set(CURL_DISABLE_TELNET ON) set(CURL_DISABLE_TFTP ON) @@ -741,9 +756,11 @@ add_custom_target(curl-lint COMMENT "Running lint checks" VERBATIM USES_TERMINAL WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/badwords-all" COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/checksrc-all.pl" COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/spacecheck.pl" DEPENDS + "${PROJECT_SOURCE_DIR}/scripts/badwords-all" "${PROJECT_SOURCE_DIR}/scripts/badwords" "${PROJECT_SOURCE_DIR}/scripts/checksrc-all.pl" "${PROJECT_SOURCE_DIR}/scripts/checksrc.pl" "${PROJECT_SOURCE_DIR}/scripts/spacecheck.pl" ) @@ -762,7 +779,7 @@ endif() # XXX(cmake): end # If we are on AIX, do the _ALL_SOURCE magic -if(CMAKE_SYSTEM_NAME STREQUAL "AIX") +if(AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX") set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_ALL_SOURCE") endif() @@ -813,17 +830,23 @@ set(HAVE_TIME_T_UNSIGNED 1) endif() -if(ENABLE_THREADED_RESOLVER) - if(WIN32) - set(USE_THREADS_WIN32 ON) - else() - find_package(Threads REQUIRED) - set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT}) - set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT}) - list(APPEND CURL_NETWORK_AND_TIME_LIBS ${CMAKE_THREAD_LIBS_INIT}) +if(NOT WIN32) + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + set(HAVE_THREADS_POSIX 1) + list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads) endif() endif() +if(ENABLE_THREADED_RESOLVER) + if(NOT WIN32 AND NOT HAVE_THREADS_POSIX) + message(FATAL_ERROR "Threaded resolver requires POSIX Threads.") + endif() + set(USE_RESOLV_THREADED ON) +elseif(USE_ARES) + set(USE_RESOLV_ARES ON) +endif() + # Check for all needed libraries if(DOS) if(WATT_ROOT) @@ -849,7 +872,7 @@ elseif(NOT WIN32 AND NOT APPLE) check_library_exists("socket" "connect" "" HAVE_LIBSOCKET) if(HAVE_LIBSOCKET) - set(CURL_NETWORK_AND_TIME_LIBS "socket" ${CURL_NETWORK_AND_TIME_LIBS}) + list(PREPEND CURL_NETWORK_AND_TIME_LIBS "socket") endif() endif() @@ -1027,6 +1050,16 @@ endif() if(HAVE_BORINGSSL OR HAVE_AWSLC) + if(NOT MSVC AND NOT ANDROID) # BoringSSL/AWS-LC MSVC builds use native Windows threads + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT) + set(HAVE_THREADS_POSIX_BORINGSSL 1) + list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads) + list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads) + elseif(OPENSSL_USE_STATIC_LIBS) + message(WARNING "BoringSSL/AWS-LC requires POSIX Threads.") + endif() + endif() if(OPENSSL_USE_STATIC_LIBS AND CMAKE_C_COMPILER_ID MATCHES "Clang") list(APPEND CURL_LIBS "stdc++") list(APPEND CMAKE_REQUIRED_LIBRARIES "stdc++") @@ -1193,7 +1226,7 @@ endif() endif() if(WIN32) - list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL + list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt") # for OpenSSL/LibreSSL BCryptGenRandom() endif() endif() if(USE_WOLFSSL) @@ -1241,9 +1274,9 @@ if(USE_WOLFSSL) curl_openssl_check_exists("wolfSSL_get_peer_certificate" HAVE_WOLFSSL_GET_PEER_CERTIFICATE) curl_openssl_check_exists("wolfSSL_UseALPN" HAVE_WOLFSSL_USEALPN) - curl_openssl_check_exists("wolfSSL_DES_ecb_encrypt" HAVE_WOLFSSL_DES_ECB_ENCRYPT) curl_openssl_check_exists("wolfSSL_BIO_new" HAVE_WOLFSSL_BIO_NEW) curl_openssl_check_exists("wolfSSL_BIO_set_shutdown" HAVE_WOLFSSL_BIO_SET_SHUTDOWN) + curl_openssl_check_exists("wc_Des_EcbEncrypt" HAVE_WC_DES_ECBENCRYPT) endif() if(USE_OPENSSL) @@ -1389,7 +1422,7 @@ find_package(LDAP MODULE) if(LDAP_FOUND) set(HAVE_LBER_H 1) - set(CURL_LIBS CURL::ldap ${CURL_LIBS}) + list(PREPEND CURL_LIBS CURL::ldap) # LDAP feature checks @@ -1426,7 +1459,7 @@ if(WIN32) option(USE_WIN32_IDN "Use WinIDN for IDN support" OFF) if(USE_WIN32_IDN) - list(APPEND CURL_LIBS "normaliz") + list(APPEND CURL_LIBS "normaliz") # for IdnToAscii(), IdnToUnicode() endif() else() set(USE_WIN32_IDN OFF) @@ -1456,7 +1489,7 @@ if(USE_LIBIDN2 AND NOT USE_APPLE_IDN AND NOT USE_WIN32_IDN) find_package(Libidn2 MODULE QUIET) if(LIBIDN2_FOUND) - set(CURL_LIBS CURL::libidn2 ${CURL_LIBS}) + list(PREPEND CURL_LIBS CURL::libidn2) set(HAVE_IDN2_H 1) set(HAVE_LIBIDN2 1) endif() @@ -1479,7 +1512,7 @@ if(CURL_USE_LIBSSH2) find_package(Libssh2 MODULE) if(LIBSSH2_FOUND) - set(CURL_LIBS CURL::libssh2 ${CURL_LIBS}) # keep it before TLS-crypto, compression + list(PREPEND CURL_LIBS CURL::libssh2) # keep it before TLS-crypto, compression set(USE_LIBSSH2 ON) endif() endif() @@ -1489,7 +1522,7 @@ mark_as_advanced(CURL_USE_LIBSSH) if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH) find_package(Libssh MODULE REQUIRED) - set(CURL_LIBS CURL::libssh ${CURL_LIBS}) # keep it before TLS-crypto, compression + list(PREPEND CURL_LIBS CURL::libssh) # keep it before TLS-crypto, compression set(USE_LIBSSH ON) endif() @@ -1548,12 +1581,6 @@ set(HAVE_UV_H ON) endif() -option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF) -if(USE_LIBRTMP) - find_package(Librtmp MODULE REQUIRED) - list(APPEND CURL_LIBS CURL::librtmp) -endif() - option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON) if(ENABLE_UNIX_SOCKETS) if(WIN32 OR DOS) @@ -1753,7 +1780,7 @@ HAVE_UNISTD_H ) if(${_variable}) - string(APPEND CURL_TEST_DEFINES " -D${_variable}") + list(APPEND CURL_TEST_DEFINES "-D${_variable}") endif() endforeach() @@ -1822,20 +1849,24 @@ check_symbol_exists("getifaddrs" "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS) # ifaddrs.h check_symbol_exists("freeaddrinfo" "${CURL_INCLUDES}" HAVE_FREEADDRINFO) # ws2tcpip.h sys/socket.h netdb.h check_function_exists("pipe" HAVE_PIPE) -check_function_exists("pipe2" HAVE_PIPE2) check_function_exists("eventfd" HAVE_EVENTFD) -check_symbol_exists("ftruncate" "unistd.h" HAVE_FTRUNCATE) check_symbol_exists("getpeername" "${CURL_INCLUDES}" HAVE_GETPEERNAME) # winsock2.h unistd.h proto/bsdsocket.h check_symbol_exists("getsockname" "${CURL_INCLUDES}" HAVE_GETSOCKNAME) # winsock2.h unistd.h proto/bsdsocket.h check_function_exists("getrlimit" HAVE_GETRLIMIT) check_function_exists("setlocale" HAVE_SETLOCALE) check_function_exists("setrlimit" HAVE_SETRLIMIT) +if(NOT APPLE) + # Apple platforms do not offer pipe2(), but the iPhone Simulator-specific + # /usr/lib/system/libsystem_sim_kernel.dylib exports it. To avoid false + # detection, omit this feature check for Apple targets. + check_function_exists("pipe2" HAVE_PIPE2) +endif() + if(NOT WIN32) check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # net/if.h check_function_exists("realpath" HAVE_REALPATH) check_function_exists("sched_yield" HAVE_SCHED_YIELD) - check_function_exists("snprintf" HAVE_SNPRINTF) # to match detection method in ./configure check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP) check_symbol_exists("stricmp" "string.h" HAVE_STRICMP) check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI) @@ -1999,9 +2030,9 @@ if(WIN32) list(APPEND CURL_NETWORK_AND_TIME_LIBS "ws2_32") if(NOT WINDOWS_STORE) - list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi") + list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi") # for if_nametoindex() endif() - list(APPEND CURL_LIBS "bcrypt") + list(APPEND CURL_LIBS "bcrypt") # for BCryptGenRandom() # We use crypto functions that are not available for UWP apps if(NOT WINDOWS_STORE) @@ -2010,10 +2041,16 @@ # Link required libraries for USE_WIN32_CRYPTO or USE_SCHANNEL if(USE_WIN32_CRYPTO OR USE_SCHANNEL) - list(APPEND CURL_LIBS "advapi32" "crypt32") + # for CryptAcquireContext(), CryptCreateHash(), CryptDestroyHash(), CryptGetHashParam(), CryptHashData(), + # CryptReleaseContext() in NTLM, md4, md5, sha256, Schannel + # for CryptDestroyKey(), CryptEncrypt(), CryptImportKey() in NTLM + list(APPEND CURL_LIBS "advapi32") + # for Cert*() in openssl.c Native CA, Schannel + # for CryptDecodeObjectEx(), CryptQueryObject(), CryptStringToBinary(), PFXImportCertStore() in Schannel + list(APPEND CURL_LIBS "crypt32") endif() if(USE_WINDOWS_SSPI) - list(APPEND CURL_LIBS "secur32") + list(APPEND CURL_LIBS "secur32") # for InitSecurityInterface() endif() endif() @@ -2033,22 +2070,14 @@ # to consume libcurl, regardless of the compiler used to build libcurl itself. if(CMAKE_C_COMPILER_ID STREQUAL "GNU") if(USE_OPENSSL AND TARGET OpenSSL::Crypto) - get_target_property(_curl_imported OpenSSL::Crypto IMPORTED) - if(_curl_imported) - add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED) - get_target_property(_curl_libname OpenSSL::Crypto LOCATION) - set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES "${_curl_libname}") - list(APPEND CURL_LIBS CURL::OpenSSL_Crypto) - endif() + add_library(CURL::OpenSSL_Crypto INTERFACE IMPORTED) + set_target_properties(CURL::OpenSSL_Crypto PROPERTIES INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) + list(APPEND CURL_LIBS CURL::OpenSSL_Crypto) endif() if(HAVE_LIBZ AND TARGET ZLIB::ZLIB) - get_target_property(_curl_imported ZLIB::ZLIB IMPORTED) - if(_curl_imported) - add_library(CURL::ZLIB INTERFACE IMPORTED) - get_target_property(_curl_libname ZLIB::ZLIB LOCATION) - set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES "${_curl_libname}") - list(APPEND CURL_LIBS CURL::ZLIB) - endif() + add_library(CURL::ZLIB INTERFACE IMPORTED) + set_target_properties(CURL::ZLIB PROPERTIES INTERFACE_LINK_LIBRARIES ZLIB::ZLIB) + list(APPEND CURL_LIBS CURL::ZLIB) endif() if(WIN32) add_library(CURL::win32_winsock INTERFACE IMPORTED) @@ -2063,12 +2092,6 @@ endif() if(CURL_LTO) - if(CMAKE_VERSION VERSION_LESS 3.9) - message(FATAL_ERROR "LTO has been requested, but your cmake version ${CMAKE_VERSION} is to old. You need at least 3.9") - endif() - - cmake_policy(SET CMP0069 NEW) - include(CheckIPOSupported) check_ipo_supported(RESULT CURL_HAS_LTO OUTPUT _lto_error LANGUAGES C) if(CURL_HAS_LTO) @@ -2164,12 +2187,12 @@ # NTLM support requires crypto functions from various SSL libs. # These conditions must match those in lib/curl_setup.h. -if(NOT CURL_DISABLE_NTLM AND +if(CURL_ENABLE_NTLM AND ((USE_OPENSSL AND HAVE_DES_ECB_ENCRYPT) OR (USE_MBEDTLS AND HAVE_MBEDTLS_DES_CRYPT_ECB) OR USE_GNUTLS OR USE_WIN32_CRYPTO OR - (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT))) + (USE_WOLFSSL AND HAVE_WC_DES_ECBENCRYPT))) set(_use_curl_ntlm_core ON) endif() @@ -2194,9 +2217,9 @@ curl_add_if("POP3S" NOT CURL_DISABLE_POP3 AND _ssl_enabled) curl_add_if("IMAP" NOT CURL_DISABLE_IMAP) curl_add_if("IMAPS" NOT CURL_DISABLE_IMAP AND _ssl_enabled) -curl_add_if("SMB" NOT CURL_DISABLE_SMB AND +curl_add_if("SMB" CURL_ENABLE_SMB AND _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) -curl_add_if("SMBS" NOT CURL_DISABLE_SMB AND _ssl_enabled AND +curl_add_if("SMBS" CURL_ENABLE_SMB AND _ssl_enabled AND _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4)) curl_add_if("SMTP" NOT CURL_DISABLE_SMTP) curl_add_if("SMTPS" NOT CURL_DISABLE_SMTP AND _ssl_enabled) @@ -2205,7 +2228,6 @@ curl_add_if("IPFS" NOT CURL_DISABLE_IPFS) curl_add_if("IPNS" NOT CURL_DISABLE_IPFS) curl_add_if("RTSP" NOT CURL_DISABLE_RTSP) -curl_add_if("RTMP" USE_LIBRTMP) curl_add_if("MQTT" NOT CURL_DISABLE_MQTT) curl_add_if("MQTTS" NOT CURL_DISABLE_MQTT AND _ssl_enabled) curl_add_if("WS" NOT CURL_DISABLE_WEBSOCKETS) @@ -2227,8 +2249,8 @@ curl_add_if("brotli" HAVE_BROTLI) curl_add_if("gsasl" USE_GSASL) curl_add_if("zstd" HAVE_ZSTD) -curl_add_if("AsynchDNS" USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32) -curl_add_if("asyn-rr" USE_ARES AND ENABLE_THREADED_RESOLVER AND USE_HTTPSRR) +curl_add_if("AsynchDNS" USE_RESOLV_ARES OR USE_RESOLV_THREADED) +curl_add_if("asyn-rr" USE_ARES AND USE_RESOLV_THREADED AND USE_HTTPSRR) curl_add_if("IDN" (HAVE_LIBIDN2 AND HAVE_IDN2_H) OR USE_WIN32_IDN OR USE_APPLE_IDN) @@ -2241,7 +2263,7 @@ (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) curl_add_if("Kerberos" NOT CURL_DISABLE_KERBEROS_AUTH AND (HAVE_GSSAPI OR USE_WINDOWS_SSPI)) -curl_add_if("NTLM" NOT CURL_DISABLE_NTLM AND +curl_add_if("NTLM" CURL_ENABLE_NTLM AND (_use_curl_ntlm_core OR USE_WINDOWS_SSPI)) curl_add_if("TLS-SRP" USE_TLS_SRP) curl_add_if("HTTP2" USE_NGHTTP2) @@ -2251,7 +2273,7 @@ OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW))) curl_add_if("Unicode" ENABLE_UNICODE) -curl_add_if("threadsafe" HAVE_ATOMIC OR (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR WIN32) +curl_add_if("threadsafe" HAVE_ATOMIC OR HAVE_THREADS_POSIX OR WIN32) curl_add_if("Debug" ENABLE_DEBUG) curl_add_if("ECH" _ssl_enabled AND HAVE_ECH) curl_add_if("HTTPSRR" _ssl_enabled AND USE_HTTPSRR) @@ -2261,11 +2283,7 @@ curl_add_if("AppleSecTrust" USE_APPLE_SECTRUST AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS)) curl_add_if("NativeCA" NOT USE_APPLE_SECTRUST AND _ssl_enabled AND CURL_CA_NATIVE) if(_items) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - list(SORT _items CASE INSENSITIVE) - else() - list(SORT _items) - endif() + list(SORT _items CASE INSENSITIVE) endif() set(CURL_SUPPORTED_FEATURES_LIST "${_items}") string(REPLACE ";" " " SUPPORT_FEATURES "${_items}") @@ -2281,11 +2299,7 @@ curl_add_if("Rustls" _ssl_enabled AND USE_RUSTLS) if(_items) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - list(SORT _items CASE INSENSITIVE) - else() - list(SORT _items) - endif() + list(SORT _items CASE INSENSITIVE) endif() string(REPLACE ";" " " SSL_BACKENDS "${_items}") message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}") @@ -2375,29 +2389,15 @@ # Assume the user will not need this information in the .pc file. continue() endif() - if(_lib MATCHES "CURL::") - # This is empty for 'CURL::*' targets and safe to ignore. - # Explicitly skip this query to avoid CMake v3.18 and older erroring out. - set(_libname "") - else() - get_target_property(_libname "${_lib}" LOCATION) - endif() - if(_libname) - list(APPEND _explicit_libs "${_libname}") - else() - get_target_property(_libs "${_lib}" INTERFACE_LINK_LIBRARIES) - if(_libs) - list(APPEND _explicit_libs "${_libs}") - endif() - get_target_property(_libdirs "${_lib}" INTERFACE_LINK_DIRECTORIES) - if(_libdirs) - list(APPEND _explicit_libdirs "${_libdirs}") - endif() - endif() - if(NOT _libname AND NOT _libs AND NOT _libdirs) + set(_libdirs "") + set(_libs "") + curl_collect_target_link_options("${_lib}") # look into the target recursively + list(APPEND _explicit_libdirs ${_libdirs}) + list(APPEND _explicit_libs ${_libs}) + if(NOT _libs AND NOT _libdirs AND NOT _lib STREQUAL Threads::Threads) message(WARNING "Bad lib in library list: ${_lib}") endif() - if(_lib STREQUAL OpenSSL::SSL) + if(_lib STREQUAL OpenSSL::SSL AND NOT HAVE_BORINGSSL) # BoringSSL does not provide openssl.pc set(_modules "openssl") elseif(_lib STREQUAL ZLIB::ZLIB) set(_modules "zlib") @@ -2578,18 +2578,20 @@ # TARGETS_EXPORT_NAME # CURL_SUPPORTED_FEATURES_LIST # CURL_SUPPORTED_PROTOCOLS_LIST + # CURL_USE_CMAKECONFIG # CURL_USE_PKGCONFIG # HAVE_BROTLI # HAVE_GSSAPI # HAVE_LIBIDN2 # HAVE_LIBZ ZLIB_VERSION_MAJOR + # HAVE_THREADS_POSIX + # HAVE_THREADS_POSIX_BORINGSSL # HAVE_ZSTD # USE_ARES # USE_BACKTRACE # USE_GNUTLS # USE_GSASL # USE_LIBPSL - # USE_LIBRTMP # USE_LIBSSH # USE_LIBSSH2 # USE_LIBUV @@ -2626,7 +2628,6 @@ "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibgsasl.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibidn2.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibpsl.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibrtmp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibssh2.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/CMake/FindLibuv.cmake" @@ -2651,12 +2652,14 @@ COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake") endif() - install(FILES "${PROJECT_SOURCE_DIR}/scripts/wcurl" - DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS - OWNER_READ OWNER_WRITE OWNER_EXECUTE - GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE) + if(BUILD_CURL_EXE) + install(FILES "${PROJECT_SOURCE_DIR}/scripts/wcurl" + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + endif() # The `-DEV` part is important string(REGEX REPLACE "([0-9]+\.[0-9]+)\.([0-9]+.*)" "\\2" CPACK_PACKAGE_VERSION_PATCH "${_curl_version}")
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h index 10351cb..3b0af7a 100644 --- a/Utilities/cmcurl/include/curl/curl.h +++ b/Utilities/cmcurl/include/curl/curl.h
@@ -220,8 +220,8 @@ #define CURL_HTTPPOST_LARGE (1 << 7) char *showfilename; /* The filename to show. If not set, the - actual filename will be used (if this - is a file part) */ + actual filename is used (if this is + a file part) */ void *userp; /* custom pointer used for HTTPPOST_CALLBACK posts */ curl_off_t contentlen; /* alternative length of contents @@ -229,8 +229,8 @@ set. Added in 7.46.0 */ }; -/* This is a return code for the progress callback that, when returned, will - signal libcurl to continue executing the default progress function */ +/* This is a return code for the progress callback that, when returned, + signals libcurl to continue executing the default progress function */ #define CURL_PROGRESSFUNC_CONTINUE 0x10000001 /* This is the CURLOPT_PROGRESSFUNCTION callback prototype. It is now @@ -267,17 +267,17 @@ #ifndef CURL_MAX_HTTP_HEADER /* The only reason to have a max limit for this is to avoid the risk of a bad - server feeding libcurl with a never-ending header that will cause reallocs + server feeding libcurl with a never-ending header that causes reallocs infinitely */ #define CURL_MAX_HTTP_HEADER (100*1024) #endif /* This is a magic return code for the write callback that, when returned, - will signal libcurl to pause receiving on the current transfer. */ + signals libcurl to pause receiving on the current transfer. */ #define CURL_WRITEFUNC_PAUSE 0x10000001 /* This is a magic return code for the write callback that, when returned, - will signal an error from the callback. */ + signals an error from the callback. */ #define CURL_WRITEFUNC_ERROR 0xFFFFFFFF typedef size_t (*curl_write_callback)(char *buffer, @@ -285,7 +285,7 @@ size_t nitems, void *outstream); -/* This callback will be called when a new resolver request is made */ +/* This callback is called when a new resolver request is made */ typedef int (*curl_resolver_start_callback)(void *resolver_state, void *reserved, void *userdata); @@ -385,11 +385,11 @@ curl_off_t offset, int origin); /* 'whence' */ -/* This is a return code for the read callback that, when returned, will - signal libcurl to immediately abort the current transfer. */ +/* This is a return code for the read callback that, when returned, + signals libcurl to immediately abort the current transfer. */ #define CURL_READFUNC_ABORT 0x10000000 -/* This is a return code for the read callback that, when returned, will - signal libcurl to pause sending data on the current transfer. */ +/* This is a return code for the read callback that, when returned, + signals libcurl to pause sending data on the current transfer. */ #define CURL_READFUNC_PAUSE 0x10000001 /* Return code for when the trailing headers' callback has terminated @@ -609,7 +609,7 @@ or wrong format */ CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ CURLE_SSH, /* 79 - error from the SSH layer, somewhat - generic so the error message will be of + generic so the error message is of interest when this has happened */ CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL @@ -627,7 +627,7 @@ CURLE_FTP_BAD_FILE_LIST, /* 87 - unable to parse FTP file list */ CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the - session will be queued */ + session is queued */ CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not match */ CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ @@ -888,7 +888,7 @@ CURLKHSTAT_REJECT, /* reject the connection, return an error */ CURLKHSTAT_DEFER, /* do not accept it, but we cannot answer right now. Causes a CURLE_PEER_FAILED_VERIFICATION error but the - connection will be left intact etc */ + connection is left intact etc */ CURLKHSTAT_FINE_REPLACE, /* accept and replace the wrong key */ CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ }; @@ -1170,12 +1170,12 @@ * bytes big. */ CURLOPT(CURLOPT_ERRORBUFFER, CURLOPTTYPE_OBJECTPOINT, 10), - /* Function that will be called to store the output (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ + /* Function that is called to store the output (instead of fwrite). The + * parameters use fwrite() syntax, make sure to follow them. */ CURLOPT(CURLOPT_WRITEFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 11), - /* Function that will be called to read the input (instead of fread). The - * parameters will use fread() syntax, make sure to follow them. */ + /* Function that is called to read the input (instead of fread). The + * parameters use fread() syntax, make sure to follow them. */ CURLOPT(CURLOPT_READFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 12), /* Time-out the read operation after this amount of seconds */ @@ -1329,7 +1329,7 @@ /* 55 = OBSOLETE */ /* DEPRECATED - * Function that will be called instead of the internal progress display + * Function that is called instead of the internal progress display * function. This function should be defined as the curl_progress_callback * prototype defines. */ CURLOPTDEPRECATED(CURLOPT_PROGRESSFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 56, @@ -1358,7 +1358,7 @@ /* Set the krb4/5 security level, this also enables krb4/5 awareness. This * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string - * is set but does not match one of these, 'private' will be used. */ + * is set but does not match one of these, 'private' is used. */ CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63, 8.17.0, "removed"), @@ -1411,8 +1411,8 @@ OK within this time, then fine... This only aborts the connect phase. */ CURLOPT(CURLOPT_CONNECTTIMEOUT, CURLOPTTYPE_LONG, 78), - /* Function that will be called to store headers (instead of fwrite). The - * parameters will use fwrite() syntax, make sure to follow them. */ + /* Function that is called to store headers (instead of fwrite). The + * parameters use fwrite() syntax, make sure to follow them. */ CURLOPT(CURLOPT_HEADERFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 79), /* Set this to force the HTTP request to get back to GET. Only really usable @@ -1437,7 +1437,7 @@ CURLOPT(CURLOPT_HTTP_VERSION, CURLOPTTYPE_VALUES, 84), /* Specifically switch on or off the FTP engine's use of the EPSV command. By - default, that one will always be attempted before the more traditional + default, that one is always attempted before the more traditional PASV command. */ CURLOPT(CURLOPT_FTP_USE_EPSV, CURLOPTTYPE_LONG, 85), @@ -1515,7 +1515,7 @@ CURLOPT(CURLOPT_UNRESTRICTED_AUTH, CURLOPTTYPE_LONG, 105), /* Specifically switch on or off the FTP engine's use of the EPRT command ( - it also disables the LPRT attempt). By default, those ones will always be + it also disables the LPRT attempt). By default, those ones are always attempted before the good old traditional PORT command. */ CURLOPT(CURLOPT_FTP_USE_EPRT, CURLOPTTYPE_LONG, 106), @@ -1555,7 +1555,7 @@ systems with support for more than one, i.e IPv4 _and_ IPv6. */ CURLOPT(CURLOPT_IPRESOLVE, CURLOPTTYPE_VALUES, 113), - /* Set this option to limit the size of a file that will be downloaded from + /* Set this option to limit the size of a file that is to be downloaded from an HTTP or FTP server. Note there is also _LARGE version which adds large file support for @@ -1572,13 +1572,13 @@ */ CURLOPT(CURLOPT_RESUME_FROM_LARGE, CURLOPTTYPE_OFF_T, 116), - /* Sets the maximum size of data that will be downloaded from + /* Sets the maximum size of data that is to be downloaded from * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. */ CURLOPT(CURLOPT_MAXFILESIZE_LARGE, CURLOPTTYPE_OFF_T, 117), /* Set this option to the filename of your .netrc file you want libcurl - to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + to parse (using the CURLOPT_NETRC option). If not set, libcurl does a poor attempt to find the user's home directory and check for a .netrc file in there. */ CURLOPT(CURLOPT_NETRC_FILE, CURLOPTTYPE_STRINGPOINT, 118), @@ -1636,7 +1636,7 @@ /* Set to non-zero to skip the IP address received in a 227 PASV FTP server response. Typically used for FTP-SSL purposes but is not restricted to - that. libcurl will then instead use the same IP address it used for the + that. libcurl is then instead using the same IP address it used for the control connection. */ CURLOPT(CURLOPT_FTP_SKIP_PASV_IP, CURLOPTTYPE_LONG, 137), @@ -1648,7 +1648,7 @@ CURLOPT(CURLOPT_LOCALPORT, CURLOPTTYPE_LONG, 139), /* Number of ports to try, including the first one set with LOCALPORT. - Thus, setting it to 1 will make no additional attempts but the first. + Thus, setting it to 1 makes no additional attempts but the first. */ CURLOPT(CURLOPT_LOCALPORTRANGE, CURLOPTTYPE_LONG, 140), @@ -1656,19 +1656,19 @@ extracting it with CURLINFO_LASTSOCKET */ CURLOPT(CURLOPT_CONNECT_ONLY, CURLOPTTYPE_LONG, 141), - /* Function that will be called to convert from the + /* Function that is called to convert from the network encoding (instead of using the iconv calls in libcurl) */ CURLOPTDEPRECATED(CURLOPT_CONV_FROM_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 142, 7.82.0, "Serves no purpose anymore"), - /* Function that will be called to convert to the + /* Function that is called to convert to the network encoding (instead of using the iconv calls in libcurl) */ CURLOPTDEPRECATED(CURLOPT_CONV_TO_NETWORK_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 143, 7.82.0, "Serves no purpose anymore"), - /* Function that will be called to convert from UTF8 + /* Function that is called to convert from UTF8 (instead of using the iconv calls in libcurl) Note that this is used only for SSL certificate processing */ CURLOPTDEPRECATED(CURLOPT_CONV_FROM_UTF8_FUNCTION, @@ -1762,9 +1762,9 @@ /* Comma separated list of hostnames defining no-proxy zones. These should match both hostnames directly, and hostnames within a domain. For - example, local.com will match local.com and www.local.com, but NOT + example, local.com matches local.com and www.local.com, but NOT notlocal.com or www.notlocal.com. For compatibility with other - implementations of this, .local.com will be considered to be the same as + implementations of this, .local.com is considered to be the same as local.com. A single * is the only valid wildcard, and effectively disables the use of proxy. */ CURLOPT(CURLOPT_NOPROXY, CURLOPTTYPE_STRINGPOINT, 177), @@ -1874,8 +1874,8 @@ in outgoing requests. The current default is 0, but it might change in a future libcurl release. - libcurl will ask for the compressed methods it knows of, and if that - is not any, it will not ask for transfer-encoding at all even if this + libcurl asks for the compressed methods it knows of, and if that + is not any, it does not ask for transfer-encoding at all even if this option is set to 1. */ CURLOPT(CURLOPT_TRANSFER_ENCODING, CURLOPTTYPE_LONG, 207), @@ -1911,7 +1911,7 @@ /* Enable/disable SASL initial response */ CURLOPT(CURLOPT_SASL_IR, CURLOPTTYPE_LONG, 218), - /* Function that will be called instead of the internal progress display + /* Function that is called instead of the internal progress display * function. This function should be defined as the curl_xferinfo_callback * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */ CURLOPT(CURLOPT_XFERINFOFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 219), @@ -2093,7 +2093,7 @@ /* Head start in milliseconds to give happy eyeballs. */ CURLOPT(CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS, CURLOPTTYPE_LONG, 271), - /* Function that will be called before a resolver request is made */ + /* Function that is called before a resolver request is made */ CURLOPT(CURLOPT_RESOLVER_START_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 272), /* User data to pass to the resolver start callback. */ @@ -2202,7 +2202,7 @@ /* used by scp/sftp to verify the host's public key */ CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311), - /* Function that will be called immediately before the initial request + /* Function that is called immediately before the initial request is made on a connection (after any protocol negotiation step). */ CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312), @@ -2348,13 +2348,13 @@ #define CURL_RTSPREQ_LAST 12L /* not used */ /* These enums are for use with the CURLOPT_NETRC option. */ -#define CURL_NETRC_IGNORED 0L /* The .netrc will never be read. +#define CURL_NETRC_IGNORED 0L /* The .netrc is never read. This is the default. */ -#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL will be preferred +#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL is preferred to one in the .netrc. */ -#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL will be ignored. +#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL is ignored. Unless one is set programmatically, the - .netrc will be queried. */ + .netrc is queried. */ enum CURL_NETRC_OPTION { /* we set a single member here, to make sure we still provide the enum, but the values to use are defined above with L suffixes */ @@ -2635,7 +2635,7 @@ /* * callback function for curl_formget() - * The void *arg pointer will be the one passed as second argument to + * The void *arg pointer is the one passed as second argument to * curl_formget(). * The character buffer passed to it must not be freed. * Should return the buffer length passed to it as the argument "len" on @@ -2650,7 +2650,7 @@ * DESCRIPTION * * Serialize a curl_httppost struct built with curl_formadd(). - * Accepts a void pointer as second argument which will be passed to + * Accepts a void pointer as second argument which is passed to * the curl_formget_callback function. * Returns 0 on success. */ @@ -2695,7 +2695,7 @@ * %XX versions). This function returns a new allocated string or NULL if an * error occurred. */ -CURL_EXTERN char *curl_easy_escape(CURL *handle, +CURL_EXTERN char *curl_easy_escape(CURL *curl, const char *string, int length); @@ -2714,9 +2714,9 @@ * Conversion Note: On non-ASCII platforms the ASCII %XX codes are * converted into the host encoding. */ -CURL_EXTERN char *curl_easy_unescape(CURL *handle, +CURL_EXTERN char *curl_easy_unescape(CURL *curl, const char *string, - int length, + int inlength, int *outlength); /* the previous version */ @@ -2756,7 +2756,7 @@ * initialize libcurl and set user defined memory management callback * functions. Users can implement memory management routines to check for * memory leaks, check for misuse of the curl library etc. User registered - * callback routines will be invoked by this library instead of the system + * callback routines is invoked by this library instead of the system * memory management routines like malloc, free etc. */ CURL_EXTERN CURLcode curl_global_init_mem(long flags, @@ -2806,8 +2806,8 @@ * * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The * backend can also be specified via the name parameter (passing -1 as id). If - * both id and name are specified, the name will be ignored. If neither id nor - * name are specified, the function will fail with CURLSSLSET_UNKNOWN_BACKEND + * both id and name are specified, the name is ignored. If neither id nor + * name are specified, the function fails with CURLSSLSET_UNKNOWN_BACKEND * and set the "avail" pointer to the NULL-terminated list of available * backends. * @@ -2818,7 +2818,7 @@ * NULL-terminated list of available SSL backends. * * The SSL backend can be set only once. If it has already been set, a - * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE. + * subsequent attempt to change it results in a CURLSSLSET_TOO_LATE. */ struct curl_ssl_backend { @@ -2842,8 +2842,8 @@ * * DESCRIPTION * - * Appends a string to a linked list. If no list exists, it will be created - * first. Returns the new list, after appending. + * Appends a string to a linked list. If no list exists, it is created first. + * Returns the new list, after appending. */ CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *list, const char *data); @@ -2991,7 +2991,8 @@ CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68, CURLINFO_HTTPAUTH_USED = CURLINFO_LONG + 69, CURLINFO_PROXYAUTH_USED = CURLINFO_LONG + 70, - CURLINFO_LASTONE = 70 + CURLINFO_SIZE_DELIVERED = CURLINFO_OFF_T + 71, + CURLINFO_LASTONE = 71 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -3075,9 +3076,8 @@ } CURLSHoption; CURL_EXTERN CURLSH *curl_share_init(void); -CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *share, CURLSHoption option, - ...); -CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *share); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *sh); /**************************************************************************** * Structures for querying information about the curl library at runtime. @@ -3245,10 +3245,10 @@ * DESCRIPTION * * The curl_easy_pause function pauses or unpauses transfers. Select the new - * state by setting the bitmask, use the convenience defines below. + * state by setting the action bitmask, use the convenience defines below. * */ -CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); +CURL_EXTERN CURLcode curl_easy_pause(CURL *curl, int action); #define CURLPAUSE_RECV (1 << 0) #define CURLPAUSE_RECV_CONT 0 @@ -3267,7 +3267,7 @@ * The curl_easy_ssls_import function adds a previously exported SSL session * to the SSL session cache of the easy handle (or the underlying share). */ -CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *handle, +CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, const unsigned char *shmac, size_t shmac_len, @@ -3276,7 +3276,7 @@ /* This is the curl_ssls_export_cb callback prototype. It * is passed to curl_easy_ssls_export() to extract SSL sessions/tickets. */ -typedef CURLcode curl_ssls_export_cb(CURL *handle, +typedef CURLcode curl_ssls_export_cb(CURL *curl, void *userptr, const char *session_key, const unsigned char *shmac, @@ -3298,7 +3298,7 @@ * callback. * */ -CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle, +CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *curl, curl_ssls_export_cb *export_fn, void *userptr); @@ -3325,18 +3325,19 @@ #else #if defined(__STDC__) && (__STDC__ >= 1) #if 0 /* Triggers clang -Wdisabled-macro-expansion, skip for CMake. */ -/* This preprocessor magic that replaces a call with the exact same call is - only done to make sure application authors pass exactly three arguments - to these functions. Use recursive macros to allow using these symbols via - the C++ global namespace '::' or reusing them as method names. */ +/* This preprocessor magic ensures that application authors pass exactly three + arguments to these functions. For compatibility with C++ global namespace + '::' and reusing these symbols as method names, while also avoiding + recursive macros, use a two-stage solution. */ +#define curl_exactly_three_arguments(a, b, c) (a, b, c) #define curl_easy_setopt(handle, opt, param) \ - curl_easy_setopt(handle, opt, param) + curl_easy_setopt curl_exactly_three_arguments(handle, opt, param) #define curl_easy_getinfo(handle, info, arg) \ - curl_easy_getinfo(handle, info, arg) + curl_easy_getinfo curl_exactly_three_arguments(handle, info, arg) #define curl_share_setopt(share, opt, param) \ - curl_share_setopt(share, opt, param) + curl_share_setopt curl_exactly_three_arguments(share, opt, param) #define curl_multi_setopt(handle, opt, param) \ - curl_multi_setopt(handle, opt, param) + curl_multi_setopt curl_exactly_three_arguments(handle, opt, param) #endif #endif /* __STDC__ >= 1 */ #endif /* gcc >= 4.3 && !__cplusplus && !CURL_DISABLE_TYPECHECK */
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h index 2ea215b..8ae3fbc 100644 --- a/Utilities/cmcurl/include/curl/curlver.h +++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -32,16 +32,16 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.19.0" +#define LIBCURL_VERSION "8.20.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 19 +#define LIBCURL_VERSION_MINOR 20 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier - parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will - always follow this syntax: + parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always + follows this syntax: 0xXXYYZZ @@ -58,7 +58,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x081300 +#define LIBCURL_VERSION_NUM 0x081400 /* * This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/easy.h b/Utilities/cmcurl/include/curl/easy.h index 0be6915..197e6e7 100644 --- a/Utilities/cmcurl/include/curl/easy.h +++ b/Utilities/cmcurl/include/curl/easy.h
@@ -51,7 +51,7 @@ * Request internal information from the curl session with this function. * The third argument MUST be pointing to the specific type of the used option * which is documented in each man page of the option. The data pointed to - * will be filled in accordingly and can be relied upon only if the function + * is filled in accordingly and can be relied upon only if the function * returns CURLE_OK. This function is intended to get used *AFTER* a performed * transfer, all results from this function are undefined until the transfer * is completed.
diff --git a/Utilities/cmcurl/include/curl/header.h b/Utilities/cmcurl/include/curl/header.h index 85c10c8..5c3281a 100644 --- a/Utilities/cmcurl/include/curl/header.h +++ b/Utilities/cmcurl/include/curl/header.h
@@ -55,14 +55,14 @@ CURLHE_NOT_BUILT_IN /* if API was disabled in the build */ } CURLHcode; -CURL_EXTERN CURLHcode curl_easy_header(CURL *easy, +CURL_EXTERN CURLHcode curl_easy_header(CURL *curl, const char *name, - size_t index, + size_t nameindex, unsigned int origin, int request, struct curl_header **hout); -CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *easy, +CURL_EXTERN struct curl_header *curl_easy_nextheader(CURL *curl, unsigned int origin, int request, struct curl_header *prev);
diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h index ad6f53f..060b73e 100644 --- a/Utilities/cmcurl/include/curl/multi.h +++ b/Utilities/cmcurl/include/curl/multi.h
@@ -132,8 +132,8 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, - CURL *curl_handle); +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *m, + CURL *curl); /* * Name: curl_multi_remove_handle() @@ -142,8 +142,8 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, - CURL *curl_handle); +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *m, + CURL *curl); /* * Name: curl_multi_fdset() @@ -154,7 +154,7 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *m, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, @@ -168,7 +168,7 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_wait(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -182,7 +182,7 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_poll(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, @@ -195,7 +195,7 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *multi_handle); +CURL_EXTERN CURLMcode curl_multi_wakeup(CURLM *m); /* * Name: curl_multi_perform() @@ -213,7 +213,7 @@ * still have occurred problems on individual transfers even when * this returns OK. */ -CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *m, int *running_handles); /* @@ -221,12 +221,12 @@ * * Desc: Cleans up and removes a whole multi stack. It does not free or * touch any individual easy handles in any way. We need to define - * in what state those handles will be if this function is called - * in the middle of a transfer. + * in what state those handles are going to be if this function is + * called in the middle of a transfer. * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *m); /* * Name: curl_multi_info_read() @@ -236,19 +236,19 @@ * error code from the transfer or the fact that a transfer is * completed. More details on these should be written down as well. * - * Repeated calls to this function will return a new struct each - * time, until a special "end of msgs" struct is returned as a signal - * that there is no more to get at this point. + * Repeated calls to this function return a new struct each time, + * until a special "end of msgs" struct is returned as a signal that + * there is no more to get at this point. * - * The data the returned pointer points to will not survive calling + * The data the returned pointer points to does not survive calling * curl_multi_cleanup(). * * The 'CURLMsg' struct is meant to be simple and only contain basic - * information. If more involved information is wanted, we will - * provide the particular "transfer handle" in that struct and that + * information. If more involved information is wanted, we provide + * the particular "transfer handle" in that struct and that * should/could/would be used in subsequent curl_easy_getinfo() calls * (or similar). The point being that we must never expose complex - * structs to applications, as then we will undoubtably get backwards + * structs to applications, as then we undoubtably get backwards * compatibility problems in the future. * * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out @@ -256,7 +256,7 @@ * queue (after this read) in the integer the second argument points * to. */ -CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue); /* @@ -308,21 +308,21 @@ * * Returns: The callback should return zero. */ -typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ +typedef int (*curl_multi_timer_callback)(CURLM *m, /* multi handle */ long timeout_ms, /* see above */ void *userp); /* private callback pointer */ CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket(CURLM *multi_handle, curl_socket_t s, int *running_handles); +curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles); -CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s, int ev_bitmask, int *running_handles); CURL_EXTERN CURLMcode CURL_DEPRECATED(7.19.5, "Use curl_multi_socket_action()") -curl_multi_socket_all(CURLM *multi_handle, int *running_handles); +curl_multi_socket_all(CURLM *m, int *running_handles); #ifndef CURL_ALLOW_OLD_MULTI_SOCKET /* This macro below was added in 7.16.3 to push users who recompile to use @@ -340,8 +340,8 @@ * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, - long *milliseconds); +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *m, + long *timeout_ms); typedef enum { /* This is the socket callback function pointer */ @@ -369,11 +369,11 @@ CURLOPT(CURLMOPT_MAX_PIPELINE_LENGTH, CURLOPTTYPE_LONG, 8), /* a connection with a content-length longer than this - will not be considered for pipelining */ + is not considered for pipelining */ CURLOPT(CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 9), /* a connection with a chunk length longer than this - will not be considered for pipelining */ + is not considered for pipelining */ CURLOPT(CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE, CURLOPTTYPE_OFF_T, 10), /* a list of site names(+port) that are blocked from pipelining */ @@ -403,20 +403,30 @@ /* This is the argument passed to the notify callback */ CURLOPT(CURLMOPT_NOTIFYDATA, CURLOPTTYPE_OBJECTPOINT, 19), + /* maximum number of threads used with threaded DNS resolver */ + CURLOPT(CURLMOPT_RESOLVE_THREADS_MAX, CURLOPTTYPE_LONG, 20), + + /* set to 1L for not joining threads when multi is cleaned up */ + CURLOPT(CURLMOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 21), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; /* Definition of bits for the CURLMOPT_NETWORK_CHANGED argument: */ -/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing - connections. Connections that are idle will be closed. Ongoing transfers - will continue with the connection they have. */ -#define CURLMNWC_CLEAR_CONNS (1L << 0) +/* - CURLMNWC_CLEAR_ALL tells libcurl to clear "everything" that could be + associated with this network, including both connections and DNS data. */ +#define CURLMNWC_CLEAR_ALL (1L << 0) -/* - CURLMNWC_CLEAR_DNS tells libcurl to prevent further reuse of existing - connections. Connections that are idle will be closed. Ongoing transfers - will continue with the connection they have. */ -#define CURLMNWC_CLEAR_DNS (1L << 0) +/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing + connections. Connections that are idle are closed. Ongoing transfers do + continue with the connection they have. */ +#define CURLMNWC_CLEAR_CONNS (1L << 1) + +/* - CURLMNWC_CLEAR_DNS tells libcurl to clear the DNS cache associated with + this multi handle. Ongoing transfers keep using their already resolved + addresses, but future name resolutions are performed again. */ +#define CURLMNWC_CLEAR_DNS (1L << 2) /* * Name: curl_multi_setopt() @@ -425,7 +435,7 @@ * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...); /* @@ -437,7 +447,7 @@ * * Returns: CURLM error code. */ -CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, void *sockp); /* @@ -450,7 +460,7 @@ * * Returns: NULL on failure, otherwise a CURL **array pointer */ -CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle); +CURL_EXTERN CURL **curl_multi_get_handles(CURLM *m); typedef enum { CURLMINFO_NONE, /* first, never use this */ @@ -479,7 +489,7 @@ * * Returns: CULRM_OK or error when value could not be obtained. */ -CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *multi_handle, +CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *m, CURLMinfo_offt info, curl_off_t *pvalue); @@ -518,7 +528,7 @@ * * Returns: CURLMcode type, general multi error code. */ -CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi, +CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *m, struct curl_waitfd *ufds, unsigned int size, unsigned int *fd_count); @@ -532,15 +542,15 @@ /* * Callback to install via CURLMOPT_NOTIFYFUNCTION. */ -typedef void (*curl_notify_callback)(CURLM *multi, +typedef void (*curl_notify_callback)(CURLM *m, unsigned int notification, CURL *easy, void *user_data); -CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *multi, +CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification); -CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *multi, +CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification); #ifdef __cplusplus
diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h index 064833d..c2dbab5 100644 --- a/Utilities/cmcurl/include/curl/system.h +++ b/Utilities/cmcurl/include/curl/system.h
@@ -45,7 +45,7 @@ * size of off_t is independent of large file support settings. Keep your * build on the safe side avoiding an off_t gating. If you have a 64-bit * off_t then take for sure that another 64-bit data type exists, dig deeper - * and you will find it. + * to find it. * */
diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h index d9a672e..d600c73 100644 --- a/Utilities/cmcurl/include/curl/typecheck-gcc.h +++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h
@@ -37,8 +37,8 @@ * order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x * when compiling with -Wlogical-op. * - * To add an option that uses the same type as an existing option, you will - * need to extend the appropriate _curl_*_option macro + * To add an option that uses the same type as an existing option, you need + * to extend the appropriate _curl_*_option macro */ #define curl_easy_setopt(handle, option, value) \ @@ -856,7 +856,7 @@ const void *); #ifdef HEADER_SSL_H /* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX - * this will of course break if we are included before OpenSSL headers... + * this of course breaks if we are included before OpenSSL headers... */ typedef CURLcode (*Wcurl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *); typedef CURLcode (*Wcurl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *);
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h index f7a42b3..b1f3a23 100644 --- a/Utilities/cmcurl/include/curl/urlapi.h +++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -114,10 +114,10 @@ /* * curl_url_cleanup() frees the CURLU handle and related resources used for - * the URL parsing. It will not free strings previously returned with the URL + * the URL parsing. It does not free strings previously returned with the URL * API. */ -CURL_EXTERN void curl_url_cleanup(CURLU *handle); +CURL_EXTERN void curl_url_cleanup(CURLU *u); /* * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new @@ -130,15 +130,15 @@ * handle. Returns error code. The returned pointer MUST be freed with * curl_free() afterwards. */ -CURL_EXTERN CURLUcode curl_url_get(const CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char **part, unsigned int flags); /* * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns - * error code. The passed in string will be copied. Passing a NULL instead of + * error code. The passed in string is copied. Passing a NULL instead of * a part string, clears that part. */ -CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what, +CURL_EXTERN CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags); /*
diff --git a/Utilities/cmcurl/include/curl/websockets.h b/Utilities/cmcurl/include/curl/websockets.h index 402a2ca..bf93715 100644 --- a/Utilities/cmcurl/include/curl/websockets.h +++ b/Utilities/cmcurl/include/curl/websockets.h
@@ -67,7 +67,7 @@ * Sends data over the websocket connection. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, +CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags);
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt index 673e9be..a11224c 100644 --- a/Utilities/cmcurl/lib/CMakeLists.txt +++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -138,9 +138,9 @@ endif() endif() -if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12) +if(SHARE_LIB_OBJECT) set(LIB_OBJECT "libcurl_object") - add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) # Requires CMake 3.12 + add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) if(WIN32) # Define CURL_STATICLIB always, to disable __declspec(dllexport) for # exported libcurl symbols. We handle exports via libcurl.def instead. @@ -168,6 +168,10 @@ if(CURL_CLANG_TIDY) set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF) endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_OBJECT} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() if(CURL_CODE_COVERAGE) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) @@ -217,6 +221,10 @@ set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF) endif() endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_STATIC} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() if(CURL_CODE_COVERAGE) set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) @@ -225,27 +233,6 @@ target_include_directories(${LIB_STATIC} INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>" "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>") - - # CMake before CMP0099 (CMake 3.17 2020-03-20) did not propagate libdirs to - # targets. It expected libs to have an absolute filename. As a workaround, - # manually apply dependency libdirs, for CMake consumers without this policy. - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.17) - cmake_policy(GET CMP0099 _has_CMP0099) # https://cmake.org/cmake/help/latest/policy/CMP0099.html - endif() - if(NOT _has_CMP0099 AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.13 AND CURL_LIBS) - set(_curl_libdirs "") - foreach(_curl_lib IN LISTS CURL_LIBS) - if(TARGET "${_curl_lib}") - get_target_property(_curl_libdir "${_curl_lib}" INTERFACE_LINK_DIRECTORIES) - if(_curl_libdir) - list(APPEND _curl_libdirs "${_curl_libdir}") - endif() - endif() - endforeach() - if(_curl_libdirs) - target_link_directories(${LIB_STATIC} INTERFACE ${_curl_libdirs}) - endif() - endif() endif() if(BUILD_SHARED_LIBS) @@ -282,14 +269,14 @@ if(CURL_CLANG_TIDY) set_target_properties(${LIB_SHARED} PROPERTIES UNITY_BUILD OFF) endif() + if(CURL_ANALYZER_CFLAGS) + set_target_properties(${LIB_SHARED} PROPERTIES UNITY_BUILD OFF) + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_ANALYZER_CFLAGS}) + endif() if(CURL_CODE_COVERAGE) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS}) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS}) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS}) - else() - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS ${CURL_COVERAGE_LDFLAGS}) - endif() + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS}) endif() target_include_directories(${LIB_SHARED} INTERFACE @@ -299,7 +286,7 @@ if(CMAKE_DLL_NAME_WITH_SOVERSION OR CYGWIN OR APPLE OR - CMAKE_SYSTEM_NAME STREQUAL "AIX" OR + AIX OR CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR @@ -360,11 +347,7 @@ set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers") check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS) if(HAVE_VERSIONED_SYMBOLS) - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}") - else() - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}") - endif() + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}") else() message(WARNING "Versioned symbols requested, but not supported by the toolchain.") endif() @@ -402,7 +385,7 @@ if(PERL_EXECUTABLE) add_custom_target(curl-optiontable COMMENT "Generating lib/easyoptions.c" VERBATIM USES_TERMINAL - COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" < "${PROJECT_SOURCE_DIR}/include/curl/curl.h" + COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" "${PROJECT_SOURCE_DIR}/include/curl/curl.h" > "${PROJECT_SOURCE_DIR}/lib/easyoptions.c" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/optiontable.pl" "${PROJECT_SOURCE_DIR}/include/curl/curl.h" )
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc index 84b4e54..d762f72 100644 --- a/Utilities/cmcurl/lib/Makefile.inc +++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -47,7 +47,6 @@ LIB_CURLX_HFILES = \ curlx/base64.h \ curlx/basename.h \ - curlx/curlx.h \ curlx/dynbuf.h \ curlx/fopen.h \ curlx/inet_ntop.h \ @@ -150,6 +149,7 @@ asyn-thrdd.c \ bufq.c \ bufref.c \ + cf-dns.c \ cf-h1-proxy.c \ cf-h2-proxy.c \ cf-haproxy.c \ @@ -172,7 +172,6 @@ curl_memrchr.c \ curl_ntlm_core.c \ curl_range.c \ - curl_rtmp.c \ curl_sasl.c \ curl_sha512_256.c \ curl_share.c \ @@ -182,6 +181,7 @@ cw-out.c \ cw-pause.c \ dict.c \ + dnscache.c \ doh.c \ dynhds.c \ easy.c \ @@ -236,6 +236,7 @@ pingpong.c \ pop3.c \ progress.c \ + protocol.c \ psl.c \ rand.c \ ratelimit.c \ @@ -259,6 +260,8 @@ system_win32.c \ telnet.c \ tftp.c \ + thrdpool.c \ + thrdqueue.c \ transfer.c \ uint-bset.c \ uint-hash.c \ @@ -276,6 +279,7 @@ asyn.h \ bufq.h \ bufref.h \ + cf-dns.h \ cf-h1-proxy.h \ cf-h2-proxy.h \ cf-haproxy.h \ @@ -304,7 +308,6 @@ curl_ntlm_core.h \ curl_printf.h \ curl_range.h \ - curl_rtmp.h \ curl_sasl.h \ curl_setup.h \ curl_sha256.h \ @@ -316,6 +319,7 @@ cw-out.h \ cw-pause.h \ dict.h \ + dnscache.h \ doh.h \ dynhds.h \ easy_lock.h \ @@ -327,6 +331,7 @@ fileinfo.h \ formdata.h \ ftp.h \ + ftp-int.h \ ftplistparser.h \ functypes.h \ getinfo.h \ @@ -362,6 +367,7 @@ pingpong.h \ pop3.h \ progress.h \ + protocol.h \ psl.h \ rand.h \ ratelimit.h \ @@ -386,6 +392,8 @@ system_win32.h \ telnet.h \ tftp.h \ + thrdpool.h \ + thrdqueue.h \ transfer.h \ uint-bset.h \ uint-hash.h \
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c index 34da3e1..81b5379 100644 --- a/Utilities/cmcurl/lib/altsvc.c +++ b/Utilities/cmcurl/lib/altsvc.c
@@ -45,6 +45,24 @@ #define H3VERSION "h3" +#if defined(DEBUGBUILD) || defined(UNITTESTS) +/* to play well with debug builds, we can *set* a fixed time this will + return */ +static time_t altsvc_debugtime(void *unused) +{ + const char *timestr = getenv("CURL_TIME"); + (void)unused; + if(timestr) { + curl_off_t val; + curlx_str_number(×tr, &val, TIME_T_MAX); + return (time_t)val; + } + return time(NULL); +} +#undef time +#define time(x) altsvc_debugtime(x) +#endif + /* Given the ALPN ID, return the name */ const char *Curl_alpnid2str(enum alpnid id) { @@ -126,6 +144,20 @@ srcport, dstport); } +/* append the new entry to the list after possibly removing an old entry + first */ +static void altsvc_append(struct altsvcinfo *asi, struct altsvc *as) +{ + while(Curl_llist_count(&asi->list) >= MAX_ALTSVC_ENTRIES) { + /* It's full. Remove the first entry in the list */ + struct Curl_llist_node *e = Curl_llist_head(&asi->list); + struct altsvc *oldas = Curl_node_elem(e); + Curl_node_remove(e); + altsvc_free(oldas); + } + Curl_llist_append(&asi->list, as, &as->node); +} + /* only returns SERIOUS errors */ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line) { @@ -162,25 +194,27 @@ curlx_str_newline(&line)) ; else { - struct altsvc *as; char dbuf[MAX_ALTSVC_DATELEN + 1]; time_t expires = 0; + time_t now = time(NULL); /* The date parser works on a null-terminated string. The maximum length is upheld by curlx_str_quotedword(). */ memcpy(dbuf, curlx_str(&date), curlx_strlen(&date)); dbuf[curlx_strlen(&date)] = 0; Curl_getdate_capped(dbuf, &expires); - as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, - (size_t)srcport, (size_t)dstport); - if(as) { - as->expires = expires; - as->prio = 0; /* not supported, set zero */ - as->persist = persist ? 1 : 0; - Curl_llist_append(&asi->list, as, &as->node); + + if(now < expires) { + struct altsvc *as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, + (size_t)srcport, (size_t)dstport); + if(as) { + as->expires = expires; + as->persist = persist ? 1 : 0; + altsvc_append(asi, as); + } + else + return CURLE_OUT_OF_MEMORY; } - else - return CURLE_OUT_OF_MEMORY; } return CURLE_OK; @@ -208,19 +242,22 @@ fp = curlx_fopen(file, FOPEN_READTEXT); if(fp) { - bool eof = FALSE; - struct dynbuf buf; - curlx_dyn_init(&buf, MAX_ALTSVC_LINE); - do { - result = Curl_get_line(&buf, fp, &eof); - if(!result) { - const char *lineptr = curlx_dyn_ptr(&buf); - curlx_str_passblanks(&lineptr); - if(curlx_str_single(&lineptr, '#')) - altsvc_add(asi, lineptr); - } - } while(!result && !eof); - curlx_dyn_free(&buf); /* free the line buffer */ + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + bool eof = FALSE; + struct dynbuf buf; + curlx_dyn_init(&buf, MAX_ALTSVC_LINE); + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + curlx_str_passblanks(&lineptr); + if(curlx_str_single(&lineptr, '#')) + altsvc_add(asi, lineptr); + } + } while(!result && !eof); + curlx_dyn_free(&buf); /* free the line buffer */ + } curlx_fclose(fp); } return result; @@ -257,7 +294,7 @@ "%s %s%s%s %u " "\"%d%02d%02d " "%02d:%02d:%02d\" " - "%u %u\n", + "%d 0\n", /* prio still always zero */ Curl_alpnid2str(as->src.alpnid), src6_pre, as->src.host, src6_post, as->src.port, @@ -268,7 +305,7 @@ stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday, stamp.tm_hour, stamp.tm_min, stamp.tm_sec, - as->persist, as->prio); + as->persist); return CURLE_OK; } @@ -427,24 +464,6 @@ } } -#if defined(DEBUGBUILD) || defined(UNITTESTS) -/* to play well with debug builds, we can *set* a fixed time this will - return */ -static time_t altsvc_debugtime(void *unused) -{ - const char *timestr = getenv("CURL_TIME"); - (void)unused; - if(timestr) { - curl_off_t val; - curlx_str_number(×tr, &val, TIME_T_MAX); - return (time_t)val; - } - return time(NULL); -} -#undef time -#define time(x) altsvc_debugtime(x) -#endif - /* * Curl_altsvc_parse() takes an incoming alt-svc response header and stores * the data correctly in the cache. @@ -588,7 +607,7 @@ else as->expires = maxage + secs; as->persist = persist; - Curl_llist_append(&asi->list, as, &as->node); + altsvc_append(asi, as); infof(data, "Added alt-svc: %.*s:%d over %s", (int)curlx_strlen(&dsthost), curlx_str(&dsthost), dstport, Curl_alpnid2str(dstalpnid));
diff --git a/Utilities/cmcurl/lib/altsvc.h b/Utilities/cmcurl/lib/altsvc.h index 2931af4..dc1740b 100644 --- a/Utilities/cmcurl/lib/altsvc.h +++ b/Utilities/cmcurl/lib/altsvc.h
@@ -28,9 +28,12 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC) #include "llist.h" +/* the maximum number of alt-svc entries kept in a single cache */ +#define MAX_ALTSVC_ENTRIES 5000 + struct althost { char *host; - unsigned short port; + uint16_t port; enum alpnid alpnid; }; @@ -39,7 +42,6 @@ struct althost dst; time_t expires; struct Curl_llist_node node; - unsigned int prio; BIT(persist); };
diff --git a/Utilities/cmcurl/lib/amigaos.c b/Utilities/cmcurl/lib/amigaos.c index d2afdbf..e4f3bfb 100644 --- a/Utilities/cmcurl/lib/amigaos.c +++ b/Utilities/cmcurl/lib/amigaos.c
@@ -118,7 +118,7 @@ * allocates memory also. */ -struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port) +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port) { struct Curl_addrinfo *ai = NULL; struct hostent *h; @@ -141,7 +141,7 @@ } } else { -#ifdef CURLRES_THREADED +#ifdef USE_RESOLV_THREADED /* gethostbyname() is not thread-safe, so we need to reopen bsdsocket * on the thread's context */
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c index 4d063d9..a4a7464 100644 --- a/Utilities/cmcurl/lib/asyn-ares.c +++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -23,12 +23,12 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES /*********************************************************************** - * Only for ares-enabled builds - * And only for functions that fulfill the asynch resolver backend API - * as defined in asyn.h, nothing else belongs in this file! + * Only for ares-enabled builds and only for functions that fulfill + * the asynch resolver backend API as defined in asyn.h, + * nothing else belongs in this file! **********************************************************************/ #ifdef HAVE_NETINET_IN_H @@ -58,35 +58,10 @@ #include "progress.h" #include "curlx/timediff.h" #include "httpsrr.h" - #include <ares.h> -#include <ares_version.h> /* really old c-ares did not include this by - itself */ -#if ARES_VERSION >= 0x010704 -#define HAVE_CARES_SERVERS_CSV 1 -#define HAVE_CARES_LOCAL_DEV 1 -#define HAVE_CARES_SET_LOCAL 1 -#endif - -#if ARES_VERSION >= 0x010b00 -#define HAVE_CARES_PORTS_CSV 1 -#endif - -#if ARES_VERSION >= 0x011000 -/* 1.16.0 or later has ares_getaddrinfo */ -#define HAVE_CARES_GETADDRINFO 1 -#else -/* How long we are willing to wait for additional parallel responses after - obtaining a "definitive" one. For old c-ares without getaddrinfo. - - This is intended to equal the c-ares default timeout. curl always uses that - default value. Unfortunately, c-ares does not expose its default timeout in - its API, but it is officially documented as 5 seconds. - - See query_completed_cb() for an explanation of how this is used. - */ -#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000 +#if ARES_VERSION < 0x011000 +#error "requires c-ares 1.16.0 or newer" #endif #ifdef USE_HTTPSRR @@ -101,7 +76,13 @@ static int ares_ver = 0; static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, - bool reset_on_null); + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, + struct Curl_resolv_async *async); +static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, + struct Curl_resolv_async *async); /* * Curl_async_global_init() - the generic low-level asynchronous name @@ -142,13 +123,18 @@ } } -static CURLcode async_ares_init(struct Curl_easy *data) +static CURLcode async_ares_init(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; int status; struct ares_options options; int optmask = ARES_OPT_SOCK_STATE_CB; - CURLcode rc = CURLE_OK; + CURLcode result = CURLE_OK; + + /* initial status - failed */ + ares->ares_status = ARES_ENOTFOUND; + async->queries_ongoing = 0; options.sock_state_cb = sock_state_cb; options.sock_state_cb_data = data; @@ -172,199 +158,168 @@ status = ares_init_options(&ares->channel, &options, optmask); if(status != ARES_SUCCESS) { ares->channel = NULL; - rc = (status == ARES_ENOMEM) ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; + result = (status == ARES_ENOMEM) ? CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT; goto out; } - rc = async_ares_set_dns_servers(data, FALSE); - if(rc && rc != CURLE_NOT_BUILT_IN) + result = async_ares_set_dns_servers(data, async); + if(result && result != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_interface(data); - if(rc && rc != CURLE_NOT_BUILT_IN) + result = async_ares_set_dns_interface(data, async); + if(result && result != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_local_ip4(data); - if(rc && rc != CURLE_NOT_BUILT_IN) + result = async_ares_set_dns_local_ip4(data, async); + if(result && result != CURLE_NOT_BUILT_IN) goto out; - rc = Curl_async_ares_set_dns_local_ip6(data); - if(rc && rc != CURLE_NOT_BUILT_IN) + result = async_ares_set_dns_local_ip6(data, async); + if(result && result != CURLE_NOT_BUILT_IN) goto out; - rc = CURLE_OK; + result = CURLE_OK; out: - if(rc && ares->channel) { + if(result && ares->channel) { ares_destroy(ares->channel); ares->channel = NULL; } - return rc; -} - -static CURLcode async_ares_init_lazy(struct Curl_easy *data) -{ - struct async_ares_ctx *ares = &data->state.async.ares; - if(!ares->channel) - return async_ares_init(data); - return CURLE_OK; -} - -CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) -{ - struct async_ares_ctx *ares = &data->state.async.ares; - CURLcode result = CURLE_OK; - if(!ares->channel) { - result = async_ares_init(data); - } - *impl = ares->channel; return result; } /* * async_ares_cleanup() cleans up async resolver data. */ -static void async_ares_cleanup(struct Curl_easy *data) +static void async_ares_cleanup(struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; - if(ares->temp_ai) { - Curl_freeaddrinfo(ares->temp_ai); - ares->temp_ai = NULL; + struct async_ares_ctx *ares = &async->ares; + if(ares->res_A) { + Curl_freeaddrinfo(ares->res_A); + ares->res_A = NULL; + } + if(ares->res_AAAA) { + Curl_freeaddrinfo(ares->res_AAAA); + ares->res_AAAA = NULL; } #ifdef USE_HTTPSRR Curl_httpsrr_cleanup(&ares->hinfo); #endif } -void Curl_async_ares_shutdown(struct Curl_easy *data) +void Curl_async_ares_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { /* c-ares has a method to "cancel" operations on a channel, but * as reported in #18216, this does not totally reset the channel * and ares may get stuck. * We need to destroy the channel and on demand create a new * one to avoid that. */ - Curl_async_ares_destroy(data); + Curl_async_ares_destroy(data, async); } -void Curl_async_ares_destroy(struct Curl_easy *data) +void Curl_async_ares_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; + (void)data; if(ares->channel) { ares_destroy(ares->channel); ares->channel = NULL; } - async_ares_cleanup(data); + async_ares_cleanup(async); } -/* - * Curl_async_pollset() is called when someone from the outside world - * (using curl_multi_fdset()) wants to get our fd_set setup. - */ - -CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps) { - struct async_ares_ctx *ares = &data->state.async.ares; - if(ares->channel) - return Curl_ares_pollset(data, ares->channel, ps); - return CURLE_OK; + struct async_ares_ctx *ares = &async->ares; + CURLcode result = CURLE_OK; + + if(ares->channel) { + result = Curl_ares_pollset(data, ares->channel, ps); + if(!result) { + timediff_t ms = Curl_ares_timeout_ms(data, async, ares->channel); + Curl_expire(data, ms, EXPIRE_ASYNC_NAME); + } + } + return result; } /* - * Curl_async_is_resolved() is called repeatedly to check if a previous + * Curl_async_take_result() is called repeatedly to check if a previous * name resolve request has completed. It should also make sure to time-out if * the operation seems to take too long. * * Returns normal CURLcode errors. */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns) +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = &async->ares; CURLcode result = CURLE_OK; - DEBUGASSERT(dns); - *dns = NULL; - - if(data->state.async.done) { - *dns = data->state.async.dns; - return ares->result; - } + DEBUGASSERT(pdns); + *pdns = NULL; + if(!ares) + return CURLE_FAILED_INIT; if(Curl_ares_perform(ares->channel, 0) < 0) { result = CURLE_UNRECOVERABLE_POLL; goto out; } -#ifndef HAVE_CARES_GETADDRINFO - /* Now that we have checked for any last minute results above, see if there - are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer - expires. */ - if(ares->num_pending && - /* This is only set to non-zero if the timer was started. */ - (ares->happy_eyeballs_dns_time.tv_sec || - ares->happy_eyeballs_dns_time.tv_usec) && - (curlx_ptimediff_ms(Curl_pgrs_now(data), - &ares->happy_eyeballs_dns_time) >= - HAPPY_EYEBALLS_DNS_TIMEOUT)) { - /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer - running. */ - memset(&ares->happy_eyeballs_dns_time, 0, - sizeof(ares->happy_eyeballs_dns_time)); - - /* Cancel the raw c-ares request, which will fire query_completed_cb() with - ARES_ECANCELLED synchronously for all pending responses. This will - leave us with res->num_pending == 0, which is perfect for the next - block. */ - ares_cancel(ares->channel); - DEBUGASSERT(ares->num_pending == 0); + if(async->queries_ongoing) { + result = CURLE_AGAIN; + goto out; } -#endif - if(!ares->num_pending) { - /* all c-ares operations done, what is the result to report? */ - Curl_resolv_unlink(data, &data->state.async.dns); - data->state.async.done = TRUE; - result = ares->result; - if(ares->ares_status == ARES_SUCCESS && !result) { - data->state.async.dns = - Curl_dnscache_mk_entry(data, &ares->temp_ai, - data->state.async.hostname, 0, - data->state.async.port, FALSE); - if(!data->state.async.dns) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } + /* all c-ares operations done, what is the result to report? */ + result = ares->result; + if(ares->ares_status == ARES_SUCCESS && !result) { + struct Curl_dns_entry *dns = + Curl_dnscache_mk_entry2(data, async->dns_queries, + &ares->res_AAAA, &ares->res_A, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } #ifdef HTTPSRR_WORKS - { + if(async->dns_queries & CURL_DNSQ_HTTPS) { + if(ares->hinfo.complete) { struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo); if(!lhrr) result = CURLE_OUT_OF_MEMORY; else - data->state.async.dns->hinfo = lhrr; + Curl_dns_entry_set_https_rr(dns, lhrr); } + else + Curl_dns_entry_set_https_rr(dns, NULL); + } #endif - if(!result) - result = Curl_dnscache_add(data, data->state.async.dns); + if(!result) { + *pdns = dns; } - /* if we have not found anything, report the proper - * CURLE_COULDNT_RESOLVE_* code */ - if(!result && !data->state.async.dns) { - const char *msg = NULL; - if(ares->ares_status != ARES_SUCCESS) - msg = ares_strerror(ares->ares_status); - result = Curl_resolver_error(data, msg); - } - - if(result) - Curl_resolv_unlink(data, &data->state.async.dns); - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound", - result, *dns ? "" : "not "); - async_ares_cleanup(data); + } + /* if we have not found anything, report the proper + * CURLE_COULDNT_RESOLVE_* code */ + if(!result && !*pdns) { + const char *msg = NULL; + if(ares->ares_status != ARES_SUCCESS) + msg = ares_strerror(ares->ares_status); + result = Curl_async_failed(data, async, msg); } + CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound", + result, *pdns ? "" : "not "); + async_ares_cleanup(async); + out: - ares->result = result; + if(result != CURLE_AGAIN) + ares->result = result; return result; } @@ -375,10 +330,10 @@ int itimeout_ms; #if TIMEDIFF_T_MAX > INT_MAX - itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : - ((timeout_ms < 0) ? -1 : (int)timeout_ms); + itimeout_ms = (timeout_ms > INT_MAX) ? INT_MAX : + ((timeout_ms < 0) ? -1 : (int)timeout_ms); #else - itimeout_ms = (int)timeout_ms; + itimeout_ms = (int)timeout_ms; #endif max_timeout.tv_sec = itimeout_ms / 1000; max_timeout.tv_usec = (itimeout_ms % 1000) * 1000; @@ -394,26 +349,91 @@ return 1000; } +static const struct Curl_addrinfo *async_ares_get_ai( + const struct Curl_addrinfo *ai, + int ai_family, + unsigned int index) +{ + unsigned int i = 0; + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } + } + return NULL; +} + +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index) +{ + struct async_ares_ctx *ares = &async->ares; + + (void)data; + switch(ai_family) { + case AF_INET: + if(ares->res_A) + return async_ares_get_ai(ares->res_A, ai_family, index); + break; + case AF_INET6: + if(ares->res_AAAA) + return async_ares_get_ai(ares->res_AAAA, ai_family, index); + break; + default: + break; + } + return NULL; +} + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + if(Curl_async_knows_https(data, async)) + return &async->ares.hinfo; + return NULL; +} + +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->dns_queries & CURL_DNSQ_HTTPS) + return ((async->dns_responses & CURL_DNSQ_HTTPS) || + !async->queries_ongoing); + return TRUE; /* we know it will never come */ +} + +#endif /* USE_HTTPSRR */ + /* * Curl_async_await() * * Waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". * - * 'entry' MUST be non-NULL. + * 'pdns' MUST be non-NULL. * * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **dns) +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + struct async_ares_ctx *ares = async ? &async->ares : NULL; struct curltime start = *Curl_pgrs_now(data); CURLcode result = CURLE_OK; - DEBUGASSERT(dns); - *dns = NULL; /* clear on entry */ + DEBUGASSERT(pdns); + *pdns = NULL; /* clear on entry */ + + if(!ares) + return CURLE_FAILED_INIT; /* Wait for the name resolve query to complete or time out. */ while(!result) { @@ -422,8 +442,8 @@ timeout_ms = Curl_timeleft_ms(data); if(!timeout_ms) { /* no applicable timeout from `data`*/ timediff_t elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &start); - if(elapsed_ms < (CURL_TIMEOUT_RESOLVE * 1000)) - timeout_ms = (CURL_TIMEOUT_RESOLVE * 1000) - elapsed_ms; + if(elapsed_ms < CURL_TIMEOUT_RESOLVE_MS) + timeout_ms = CURL_TIMEOUT_RESOLVE_MS - elapsed_ms; else timeout_ms = -1; } @@ -439,8 +459,10 @@ break; } - result = Curl_async_is_resolved(data, dns); - if(result || data->state.async.done) + result = Curl_async_take_result(data, async, pdns); + if(result == CURLE_AGAIN) + result = CURLE_OK; + else if(result || *pdns) break; if(Curl_pgrsUpdate(data)) { @@ -449,148 +471,17 @@ } } - /* Operation complete, if the lookup was successful we now have the entry - in the cache. */ - data->state.async.done = TRUE; - *dns = data->state.async.dns; - if(result) ares_cancel(ares->channel); return result; } -#ifndef HAVE_CARES_GETADDRINFO - -/* Connects results to the list */ -static void async_addr_concat(struct Curl_addrinfo **pbase, - struct Curl_addrinfo *ai) -{ - if(!ai) - return; - - /* When adding `ai` to an existing address list, we prefer ipv6 - * to be in front. */ -#ifdef USE_IPV6 /* CURLRES_IPV6 */ - if(*pbase && (*pbase)->ai_family == PF_INET6) { - /* ipv6 already in front, append `ai` */ - struct Curl_addrinfo *tail = *pbase; - while(tail->ai_next) - tail = tail->ai_next; - tail->ai_next = ai; - } - else -#endif /* CURLRES_IPV6 */ - { - /* prepend to the (possibly) existing list. */ - struct Curl_addrinfo *tail = ai; - while(tail->ai_next) - tail = tail->ai_next; - tail->ai_next = *pbase; - *pbase = ai; - } -} - -/* - * ares_query_completed_cb() is the callback that ares will call when - * the host query initiated by ares_gethostbyname() from - * Curl_async_getaddrinfo(), when using ares, is completed either - * successfully or with failure. - */ -static void async_ares_hostbyname_cb(void *user_data, - int status, - int timeouts, - struct hostent *hostent) -{ - struct Curl_easy *data = (struct Curl_easy *)user_data; - struct async_ares_ctx *ares = &data->state.async.ares; - - (void)timeouts; /* ignored */ - - if(ARES_EDESTRUCTION == status) - return; - - if(ARES_SUCCESS == status) { - ares->ares_status = status; /* one success overrules any error */ - async_addr_concat(&ares->temp_ai, - Curl_he2ai(hostent, data->state.async.port)); - } - else if(ares->ares_status != ARES_SUCCESS) { - /* no success so far, remember last error */ - ares->ares_status = status; - } - - ares->num_pending--; - - CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, " - "addr=%sfound", - status, ares->num_pending, ares->temp_ai ? "" : "not "); - /* If there are responses still pending, we presume they must be the - complementary IPv4 or IPv6 lookups that we started in parallel in - Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a - "definitive" response from one of a set of parallel queries, we need to - think about how long we are willing to wait for more responses. */ - if(ares->num_pending - /* Only these c-ares status values count as "definitive" for these - purposes. For example, ARES_ENODATA is what we expect when there is - no IPv6 entry for a domain name, and that is not a reason to get more - aggressive in our timeouts for the other response. Other errors are - either a result of bad input (which should affect all parallel - requests), local or network conditions, non-definitive server - responses, or us cancelling the request. */ - && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) { - /* Right now, there can only be up to two parallel queries, so do not - bother handling any other cases. */ - DEBUGASSERT(ares->num_pending == 1); - - /* it is possible that one of these parallel queries could succeed - quickly, but the other could always fail or timeout (when we are - talking to a pool of DNS servers that can only successfully resolve - IPv4 address, for example). - - it is also possible that the other request could always take longer - because it needs more time or only the second DNS server can fulfill it - successfully. But, to align with the philosophy of Happy Eyeballs, we - do not want to wait _too_ long or users will think requests are slow - when IPv6 lookups do not actually work (but IPv4 ones do). - - So, now that we have a usable answer (some IPv4 addresses, some IPv6 - addresses, or "no such domain"), we start a timeout for the remaining - pending responses. Even though it is typical that this resolved - request came back quickly, that need not be the case. It might be that - this completing request did not get a result from the first DNS - server or even the first round of the whole DNS server pool. So it - could already be a long time after we issued the DNS queries in - the first place. Without modifying c-ares, we cannot know exactly - where in its retry cycle we are. We could guess based on how much - time has gone by, but it does not really matter. Happy Eyeballs tells - us that, given usable information in hand, we do not want to - wait "too much longer" after we get a result. - - We wait an additional amount of time equal to the default c-ares - query timeout. That is enough time for a typical parallel response to - arrive without being "too long". Even on a network where one of the two - types of queries is failing or timing out constantly, this will usually - mean we wait a total of the default c-ares timeout (5 seconds) plus the - round trip time for the successful request, which seems bearable. The - downside is that c-ares might race with us to issue one more retry - before we give up, but it seems better to "waste" that request instead - of trying to guess the perfect timeout to prevent it. After all, we do - not even know where in the c-ares retry cycle each request is. - */ - ares->happy_eyeballs_dns_time = *Curl_pgrs_now(data); - Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT, EXPIRE_HAPPY_EYEBALLS_DNS); - } -} - -#else -/* c-ares 1.16.0 or later */ - /* * async_ares_node2addr() converts an address list provided by c-ares * to an internal libcurl compatible list. */ -static struct Curl_addrinfo * -async_ares_node2addr(struct ares_addrinfo_node *node) +static struct Curl_addrinfo *async_ares_node2addr( + struct ares_addrinfo_node *node) { /* traverse the ares_addrinfo_node list */ struct ares_addrinfo_node *ai; @@ -660,46 +551,70 @@ return cafirst; } -static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts, - struct ares_addrinfo *ares_ai) +static void async_ares_A_cb(void *user_data, int status, int timeouts, + struct ares_addrinfo *ares_ai) { - struct Curl_easy *data = (struct Curl_easy *)user_data; - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + (void)timeouts; - if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ - ares->ares_status = status; + if(!async) + return; + + async->dns_responses |= CURL_DNSQ_A; + async->queries_ongoing--; + async->done = !async->queries_ongoing; if(status == ARES_SUCCESS) { - ares->temp_ai = async_ares_node2addr(ares_ai->nodes); + ares->ares_status = ARES_SUCCESS; + ares->res_A = async_ares_node2addr(ares_ai->nodes); ares_freeaddrinfo(ares_ai); } - ares->num_pending--; - CURL_TRC_DNS(data, "ares: addrinfo done, query status=%d, " - "overall status=%d, pending=%d, addr=%sfound", - status, ares->ares_status, ares->num_pending, - ares->temp_ai ? "" : "not "); + else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ + ares->ares_status = status; } -#endif +#ifdef CURLRES_IPV6 +static void async_ares_AAAA_cb(void *user_data, int status, int timeouts, + struct ares_addrinfo *ares_ai) +{ + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + + (void)timeouts; + if(!async) + return; + + async->dns_responses |= CURL_DNSQ_AAAA; + async->queries_ongoing--; + async->done = !async->queries_ongoing; + if(status == ARES_SUCCESS) { + ares->ares_status = ARES_SUCCESS; + ares->res_AAAA = async_ares_node2addr(ares_ai->nodes); + ares_freeaddrinfo(ares_ai); + } + else if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */ + ares->ares_status = status; +} +#endif /* CURLRES_IPV6 */ #ifdef USE_HTTPSRR static void async_ares_rr_done(void *user_data, ares_status_t status, size_t timeouts, const ares_dns_record_t *dnsrec) { - struct Curl_easy *data = user_data; - struct async_ares_ctx *ares = &data->state.async.ares; + struct Curl_resolv_async *async = user_data; + struct async_ares_ctx *ares = async ? &async->ares : NULL; + + if(!async) + return; (void)timeouts; - --ares->num_pending; - CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, " - "dnsres=%sfound", - status, ares->num_pending, - (dnsrec && - ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ? - "" : "not "); + async->dns_responses |= CURL_DNSQ_HTTPS; + async->queries_ongoing--; + async->done = !async->queries_ongoing; if((ARES_SUCCESS != status) || !dnsrec) return; - ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo); + ares->result = Curl_httpsrr_from_ares(dnsrec, &ares->hinfo); } #endif /* USE_HTTPSRR */ @@ -708,115 +623,92 @@ * * Starts a name resolve for the given hostname and port number. */ -CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, - int port, int ip_version) +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; -#ifdef USE_HTTPSRR - char *rrname = NULL; -#endif + struct async_ares_ctx *ares = &async->ares; + char service[12]; + int socktype; + CURLcode result = CURLE_OK; - if(async_ares_init_lazy(data)) - return CURLE_FAILED_INIT; - - data->state.async.done = FALSE; /* not done */ - data->state.async.dns = NULL; /* clear */ - data->state.async.port = port; - data->state.async.ip_version = ip_version; - data->state.async.hostname = curlx_strdup(hostname); - if(!data->state.async.hostname) - return CURLE_OUT_OF_MEMORY; -#ifdef USE_HTTPSRR - if(port != 443) { - rrname = curl_maprintf("_%d_.https.%s", port, hostname); - if(!rrname) - return CURLE_OUT_OF_MEMORY; + if(ares->channel) { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + goto out; } -#endif - /* initial status - failed */ - ares->ares_status = ARES_ENOTFOUND; - ares->result = CURLE_OK; + result = async_ares_init(data, async); + if(result) + goto out; + + result = Curl_resolv_announce_start(data, ares->channel); + if(result) + goto out; #if defined(CURLVERBOSE) && ARES_VERSION >= 0x011800 /* >= v1.24.0 */ if(CURL_TRC_DNS_is_verbose(data)) { char *csv = ares_get_servers_csv(ares->channel); - CURL_TRC_DNS(data, "asyn-ares: servers=%s", csv); + CURL_TRC_DNS(data, "ares: servers=%s", csv); ares_free_string(csv); } #endif -#ifdef HAVE_CARES_GETADDRINFO - { - struct ares_addrinfo_hints hints; - char service[12]; - int pf = PF_INET; - memset(&hints, 0, sizeof(hints)); + curl_msnprintf(service, sizeof(service), "%d", async->port); + socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? + SOCK_STREAM : SOCK_DGRAM; + #ifdef CURLRES_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { - /* The stack seems to be IPv6-enabled */ - if(ip_version == CURL_IPRESOLVE_V6) - pf = PF_INET6; - else - pf = PF_UNSPEC; - } -#endif /* CURLRES_IPV6 */ - CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s", - (pf == PF_UNSPEC) ? "A+AAAA" : - ((pf == PF_INET) ? "A" : "AAAA")); - hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? - SOCK_STREAM : SOCK_DGRAM; - /* Since the service is a numerical one, set the hint flags - * accordingly to save a call to getservbyname in inside C-Ares - */ + if(async->dns_queries & CURL_DNSQ_AAAA) { + struct ares_addrinfo_hints hints; + + memset(&hints, 0, sizeof(hints)); + CURL_TRC_DNS(data, "ares: query AAAA records for %s", async->hostname); + hints.ai_family = PF_INET6; + hints.ai_socktype = socktype; hints.ai_flags = ARES_AI_NUMERICSERV; - curl_msnprintf(service, sizeof(service), "%d", port); - ares->num_pending = 1; - ares_getaddrinfo(ares->channel, data->state.async.hostname, - service, &hints, async_ares_addrinfo_cb, data); + async->queries_ongoing++; + ares_getaddrinfo(ares->channel, async->hostname, + service, &hints, async_ares_AAAA_cb, async); } -#else +#endif /* CURLRES_IPV6 */ -#if ARES_VERSION >= 0x010601 /* IPv6 supported since 1.6.1 */ - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { - /* The stack seems to be IPv6-enabled */ - /* areschannel is already setup in the Curl_open() function */ - CURL_TRC_DNS(data, "asyn-ares: fire off query for A"); - ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET, - async_ares_hostbyname_cb, data); - CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA"); - ares->num_pending = 2; - ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6, - async_ares_hostbyname_cb, data); + if(async->dns_queries & CURL_DNSQ_A) { + struct ares_addrinfo_hints hints; + + memset(&hints, 0, sizeof(hints)); + CURL_TRC_DNS(data, "ares: query A records for %s", async->hostname); + hints.ai_family = PF_INET; + hints.ai_socktype = socktype; + hints.ai_flags = ARES_AI_NUMERICSERV; + async->queries_ongoing++; + ares_getaddrinfo(ares->channel, async->hostname, + service, &hints, async_ares_A_cb, async); } - else -#endif - { - /* areschannel is already setup in the Curl_open() function */ - CURL_TRC_DNS(data, "asyn-ares: fire off query for A"); - ares->num_pending = 1; - ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET, - async_ares_hostbyname_cb, data); - } -#endif + #ifdef USE_HTTPSRR - { - CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR: %s", - rrname ? rrname : data->state.async.hostname); - memset(&ares->hinfo, 0, sizeof(ares->hinfo)); - ares->hinfo.port = -1; + memset(&ares->hinfo, 0, sizeof(ares->hinfo)); + if(async->dns_queries & CURL_DNSQ_HTTPS) { + char *rrname = NULL; + if(async->port != 443) { + rrname = curl_maprintf("_%d._https.%s", async->port, async->hostname); + if(!rrname) + return CURLE_OUT_OF_MEMORY; + } + CURL_TRC_DNS(data, "ares: query HTTPS records for %s", + rrname ? rrname : async->hostname); ares->hinfo.rrname = rrname; - ares->num_pending++; /* one more */ + async->queries_ongoing++; ares_query_dnsrec(ares->channel, - rrname ? rrname : data->state.async.hostname, + rrname ? rrname : async->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, - async_ares_rr_done, data, NULL); + async_ares_rr_done, async, NULL); } -#endif +#endif /* USE_HTTPSRR */ - return CURLE_OK; +out: + ares->result = result; + return result ? result : (async->queries_ongoing ? CURLE_AGAIN : CURLE_OK); } /* Set what DNS server are is to use. This is called in 2 situations: @@ -826,33 +718,24 @@ * 2. When we lazy init the ares channel and NULL means that there * are no preferences and we do not reset any existing channel. */ static CURLcode async_ares_set_dns_servers(struct Curl_easy *data, - bool reset_on_null) + struct Curl_resolv_async *async) { - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; CURLcode result = CURLE_NOT_BUILT_IN; const char *servers = data->set.str[STRING_DNS_SERVERS]; int ares_result = ARES_SUCCESS; -#if defined(DEBUGBUILD) && defined(HAVE_CARES_SERVERS_CSV) +#ifdef DEBUGBUILD if(getenv("CURL_DNS_SERVER")) servers = getenv("CURL_DNS_SERVER"); #endif - if(!servers) { - if(reset_on_null) { - Curl_async_destroy(data); - } + if(!servers) return CURLE_OK; - } -#ifdef HAVE_CARES_SERVERS_CSV /* if channel is not there, this is a parameter check */ - if(ares->channel) -#ifdef HAVE_CARES_PORTS_CSV + if(ares && ares->channel) ares_result = ares_set_servers_ports_csv(ares->channel, servers); -#else - ares_result = ares_set_servers_csv(ares->channel, servers); -#endif switch(ares_result) { case ARES_SUCCESS: result = CURLE_OK; @@ -868,43 +751,29 @@ result = CURLE_BAD_FUNCTION_ARGUMENT; break; } -#else /* c-ares version too old! */ - (void)data; - (void)(ares_result); -#endif return result; } -CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data) +static CURLcode async_ares_set_dns_interface(struct Curl_easy *data, + struct Curl_resolv_async *async) { - return async_ares_set_dns_servers(data, TRUE); -} - -CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data) -{ -#ifdef HAVE_CARES_LOCAL_DEV - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; const char *interf = data->set.str[STRING_DNS_INTERFACE]; if(!interf) interf = ""; /* if channel is not there, this is a parameter check */ - if(ares->channel) + if(ares && ares->channel) ares_set_local_dev(ares->channel, interf); return CURLE_OK; -#else /* c-ares version too old! */ - (void)data; - (void)interf; - return CURLE_NOT_BUILT_IN; -#endif } -CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data) +static CURLcode async_ares_set_dns_local_ip4(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef HAVE_CARES_SET_LOCAL - struct async_ares_ctx *ares = &data->state.async.ares; + struct async_ares_ctx *ares = async ? &async->ares : NULL; struct in_addr a4; const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4]; @@ -919,21 +788,17 @@ } /* if channel is not there yet, this is a parameter check */ - if(ares->channel) + if(ares && ares->channel) ares_set_local_ip4(ares->channel, ntohl(a4.s_addr)); return CURLE_OK; -#else /* c-ares version too old! */ - (void)data; - (void)local_ip4; - return CURLE_NOT_BUILT_IN; -#endif } -CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data) +static CURLcode async_ares_set_dns_local_ip6(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6) - struct async_ares_ctx *ares = &data->state.async.ares; +#ifdef USE_IPV6 + struct async_ares_ctx *ares = async ? &async->ares : NULL; unsigned char a6[INET6_ADDRSTRLEN]; const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6]; @@ -949,14 +814,15 @@ } /* if channel is not there, this is a parameter check */ - if(ares->channel) + if(ares && ares->channel) ares_set_local_ip6(ares->channel, a6); return CURLE_OK; -#else /* c-ares version too old! */ +#else /* no IPv6 support */ (void)data; + (void)async; return CURLE_NOT_BUILT_IN; #endif } -#endif /* CURLRES_ARES */ +#endif /* USE_RESOLV_ARES */
diff --git a/Utilities/cmcurl/lib/asyn-base.c b/Utilities/cmcurl/lib/asyn-base.c index 44bf98c..62cb0ef 100644 --- a/Utilities/cmcurl/lib/asyn-base.c +++ b/Utilities/cmcurl/lib/asyn-base.c
@@ -39,12 +39,14 @@ #ifdef USE_ARES #include <ares.h> -#include <ares_version.h> /* really old c-ares did not include it by itself */ #endif #include "urldata.h" +#include "connect.h" +#include "curl_trc.h" #include "hostip.h" #include "multiif.h" +#include "progress.h" #include "select.h" #include "url.h" @@ -53,10 +55,21 @@ **********************************************************************/ #ifdef CURLRES_ASYNCH +timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + if(async->timeout_ms) { + timediff_t elapsed_ms = + curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + return async->timeout_ms - elapsed_ms; + } + return Curl_timeleft_ms(data); +} + #ifdef USE_ARES -#if ARES_VERSION < 0x010600 -#error "requires c-ares 1.6.0 or newer" +#if ARES_VERSION < 0x011000 +#error "requires c-ares 1.16.0 or newer" #endif /* @@ -71,12 +84,8 @@ ares_channel channel, struct easy_pollset *ps) { - struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 }; - struct timeval timebuf; curl_socket_t sockets[16]; /* ARES documented limit */ unsigned int bitmap, i; - struct timeval *timeout; - timediff_t milli; CURLcode result = CURLE_OK; DEBUGASSERT(channel); @@ -97,15 +106,33 @@ if(result) return result; } - - timeout = ares_timeout(channel, &maxtime, &timebuf); - if(!timeout) - timeout = &maxtime; - milli = curlx_tvtoms(timeout); - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); return result; } +timediff_t Curl_ares_timeout_ms(struct Curl_easy *data, + struct Curl_resolv_async *async, + ares_channel channel) +{ + timediff_t async_timeout_ms; + + DEBUGASSERT(channel); + if(!channel) + return -1; + + async_timeout_ms = Curl_async_timeleft_ms(data, async); + if((async_timeout_ms > 0) && (async_timeout_ms < INT_MAX)) { + struct timeval timebuf; + struct timeval *timeout; + struct timeval end = { (int)async_timeout_ms / 1000, + ((int)async_timeout_ms % 1000) * 1000 }; + + timeout = ares_timeout(channel, &end, &timebuf); + if(timeout) + return curlx_tvtoms(timeout); + } + return async_timeout_ms; +} + /* * Curl_ares_perform() * @@ -178,32 +205,61 @@ #include "doh.h" -void Curl_async_shutdown(struct Curl_easy *data) +void Curl_async_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef CURLRES_ARES - Curl_async_ares_shutdown(data); + if(async) { + CURL_TRC_DNS(data, "[%u] shutdown async", async->id); + async->shutdown = TRUE; +#ifdef USE_RESOLV_ARES + Curl_async_ares_shutdown(data, async); #endif -#ifdef CURLRES_THREADED - Curl_async_thrdd_shutdown(data); +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_shutdown(data, async); #endif #ifndef CURL_DISABLE_DOH - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); #endif - Curl_safefree(data->state.async.hostname); + } } -void Curl_async_destroy(struct Curl_easy *data) +void Curl_async_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) { -#ifdef CURLRES_ARES - Curl_async_ares_destroy(data); + if(async) { + CURL_TRC_DNS(data, "[%u] destroy async", async->id); + async->shutdown = TRUE; +#ifdef USE_RESOLV_ARES + Curl_async_ares_destroy(data, async); #endif -#ifdef CURLRES_THREADED - Curl_async_thrdd_destroy(data); +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_destroy(data, async); #endif #ifndef CURL_DISABLE_DOH - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); #endif - Curl_safefree(data->state.async.hostname); + curlx_safefree(async); + } +} + +CURLcode Curl_async_failed(struct Curl_easy *data, + struct Curl_resolv_async *async, + const char *detail) +{ + const char *host_or_proxy = "host"; + CURLcode result = CURLE_COULDNT_RESOLVE_HOST; + +#ifndef CURL_DISABLE_PROXY + if(async->for_proxy) { + host_or_proxy = "proxy"; + result = CURLE_COULDNT_RESOLVE_PROXY; + } +#endif + + failf(data, "Could not resolve %s: %s%s%s%s", + host_or_proxy, async->hostname, + detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); + return result; } #endif /* USE_CURL_ASYNC */
diff --git a/Utilities/cmcurl/lib/asyn-thrdd.c b/Utilities/cmcurl/lib/asyn-thrdd.c index c3ed8ea..90f055c 100644 --- a/Utilities/cmcurl/lib/asyn-thrdd.c +++ b/Utilities/cmcurl/lib/asyn-thrdd.c
@@ -26,9 +26,7 @@ /*********************************************************************** * Only for threaded name resolves builds **********************************************************************/ -#ifdef CURLRES_THREADED - -#include "socketpair.h" +#ifdef USE_RESOLV_THREADED #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> @@ -44,10 +42,6 @@ #include <inet.h> #endif -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) -#include <pthread.h> -#endif - #ifdef HAVE_GETADDRINFO #define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */ #else @@ -64,7 +58,11 @@ #include "multiif.h" #include "curl_threads.h" #include "progress.h" +#include "rand.h" #include "select.h" +#include "thrdqueue.h" +#include "curlx/strparse.h" +#include "curlx/wait.h" #ifdef USE_ARES #include <ares.h> @@ -101,229 +99,93 @@ #endif } -static void async_thrdd_destroy(struct Curl_easy *data); -static void async_thrdd_shutdown(struct Curl_easy *data); +#ifdef CURLVERBOSE +#define CURL_ASYN_ITEM_DESC_LEN 64 +#define async_item_description(x) (x)->description +#else +#define async_item_description(x) NULL +#endif -CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl) -{ - (void)data; - *impl = NULL; - return CURLE_OK; -} +struct async_thrdd_item { + struct Curl_addrinfo *res; +#ifdef CURLVERBOSE + char description[CURL_ASYN_ITEM_DESC_LEN]; +#endif + int sock_error; + uint32_t mid; + uint32_t resolv_id; + uint16_t port; + uint8_t transport; + uint8_t dns_queries; +#ifdef DEBUGBUILD + uint32_t delay_ms; + uint32_t delay_fail_ms; +#endif + char hostname[1]; +}; /* Give up reference to add_ctx */ -static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx, - struct Curl_easy *data) +static void async_thrdd_item_destroy(struct async_thrdd_item *item) { - struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx; - bool destroy; - - if(!addr_ctx) - return; - - Curl_mutex_acquire(&addr_ctx->mutx); - if(!data) /* called by resolving thread */ - addr_ctx->thrd_done = TRUE; - - DEBUGASSERT(addr_ctx->ref_count); - --addr_ctx->ref_count; - destroy = !addr_ctx->ref_count; - Curl_mutex_release(&addr_ctx->mutx); - - if(destroy) { - Curl_mutex_destroy(&addr_ctx->mutx); - curlx_free(addr_ctx->hostname); - if(addr_ctx->res) - Curl_freeaddrinfo(addr_ctx->res); - Curl_wakeup_destroy(addr_ctx->sock_pair); - curlx_free(addr_ctx); + if(item) { + if(item->res) + Curl_freeaddrinfo(item->res); + curlx_free(item); } - *paddr_ctx = NULL; } /* Initialize context for threaded resolver */ -static struct async_thrdd_addr_ctx * -addr_ctx_create(struct Curl_easy *data, - const char *hostname, int port, - const struct addrinfo *hints) +static struct async_thrdd_item *async_thrdd_item_create( + struct Curl_easy *data, + uint32_t resolv_id, uint8_t dns_queries, + const char *hostname, uint16_t port, + uint8_t transport) { - struct async_thrdd_addr_ctx *addr_ctx = curlx_calloc(1, sizeof(*addr_ctx)); - if(!addr_ctx) + size_t hostlen = strlen(hostname); + struct async_thrdd_item *item; + + item = curlx_calloc(1, sizeof(*item) + hostlen); + if(!item) return NULL; - addr_ctx->thread_hnd = curl_thread_t_null; - addr_ctx->port = port; - addr_ctx->ref_count = 1; + if(hostlen) /* NUL byte of name already in struct size */ + memcpy(item->hostname, hostname, hostlen); + item->mid = data->mid; + item->resolv_id = resolv_id; + item->dns_queries = dns_queries; + item->port = port; + item->transport = transport; -#ifdef HAVE_GETADDRINFO - DEBUGASSERT(hints); - addr_ctx->hints = *hints; -#else - (void)hints; +#ifdef CURLVERBOSE + curl_msnprintf(item->description, sizeof(item->description), + "[%" FMT_OFF_T "/%u] %s %s:%u", + data->id, item->resolv_id, + Curl_resolv_query_str(dns_queries), + item->hostname, item->port); #endif - Curl_mutex_init(&addr_ctx->mutx); - -#ifndef CURL_DISABLE_SOCKETPAIR - /* create socket pair or pipe */ - if(Curl_wakeup_init(addr_ctx->sock_pair, FALSE) < 0) { - addr_ctx->sock_pair[0] = CURL_SOCKET_BAD; - addr_ctx->sock_pair[1] = CURL_SOCKET_BAD; - goto err_exit; - } -#endif - addr_ctx->sock_error = 0; - - /* Copying hostname string because original can be destroyed by parent - * thread during gethostbyname execution. - */ - addr_ctx->hostname = curlx_strdup(hostname); - if(!addr_ctx->hostname) - goto err_exit; - - return addr_ctx; - -err_exit: - addr_ctx_unlink(&addr_ctx, data); - return NULL; -} - -#ifdef HAVE_GETADDRINFO - -/* - * getaddrinfo_thread() resolves a name and then exits. - * - * For builds without ARES, but with USE_IPV6, create a resolver thread - * and wait on it. - */ -static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg) -{ - struct async_thrdd_addr_ctx *addr_ctx = arg; - curl_bit do_abort; - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); - - if(!do_abort) { - char service[12]; - int rc; - - curl_msnprintf(service, sizeof(service), "%d", addr_ctx->port); - - rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service, - &addr_ctx->hints, &addr_ctx->res); - - if(rc) { - addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc; - if(addr_ctx->sock_error == 0) - addr_ctx->sock_error = RESOLVER_ENOMEM; - } - else { - Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port); - } - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!do_abort) { - /* Thread is done, notify transfer */ - int err = Curl_wakeup_signal(addr_ctx->sock_pair); - if(err) { - /* update sock_error to errno */ - addr_ctx->sock_error = err; +#ifdef DEBUGBUILD + { + const char *p = getenv("CURL_DBG_RESOLV_DELAY"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + item->delay_ms = (uint32_t)l; } } -#endif - } - - addr_ctx_unlink(&addr_ctx, NULL); - return 0; -} - -#else /* HAVE_GETADDRINFO */ - -/* - * gethostbyname_thread() resolves a name and then exits. - */ -static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg) -{ - struct async_thrdd_addr_ctx *addr_ctx = arg; - bool do_abort; - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); - - if(!do_abort) { - addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port); - if(!addr_ctx->res) { - addr_ctx->sock_error = SOCKERRNO; - if(addr_ctx->sock_error == 0) - addr_ctx->sock_error = RESOLVER_ENOMEM; - } - - Curl_mutex_acquire(&addr_ctx->mutx); - do_abort = addr_ctx->do_abort; - Curl_mutex_release(&addr_ctx->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!do_abort) { - int err = Curl_wakeup_signal(addr_ctx->sock_pair); - if(err) { - /* update sock_error to errno */ - addr_ctx->sock_error = err; + p = getenv("CURL_DBG_RESOLV_FAIL_DELAY"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + unsigned char c = 0; + Curl_rand_bytes(data, FALSE, &c, 1); + item->delay_fail_ms = (uint32_t)l + c; } } -#endif } - - addr_ctx_unlink(&addr_ctx, NULL); - return 0; -} - -#endif /* HAVE_GETADDRINFO */ - -/* - * async_thrdd_destroy() cleans up async resolver data and thread handle. - */ -static void async_thrdd_destroy(struct Curl_easy *data) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr = thrdd->addr; - -#ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - ares_destroy(thrdd->rr.channel); - thrdd->rr.channel = NULL; - } - Curl_httpsrr_cleanup(&thrdd->rr.hinfo); #endif - if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) { - curl_bit done; - - Curl_mutex_acquire(&addr->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!addr->do_abort) - Curl_multi_will_close(data, addr->sock_pair[0]); -#endif - addr->do_abort = TRUE; - done = addr->thrd_done; - Curl_mutex_release(&addr->mutx); - - if(done) { - Curl_thread_join(&addr->thread_hnd); - CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined"); - } - else { - /* thread is still running. Detach it. */ - Curl_thread_destroy(&addr->thread_hnd); - CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached"); - } - } - /* release our reference to the shared context */ - addr_ctx_unlink(&thrdd->addr, data); + return item; } #ifdef USE_HTTPSRR_ARES @@ -332,25 +194,30 @@ size_t timeouts, const ares_dns_record_t *dnsrec) { - struct Curl_easy *data = user_data; - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; + struct Curl_resolv_async *async = user_data; + struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; (void)timeouts; - thrdd->rr.done = TRUE; - if((ARES_SUCCESS != status) || !dnsrec) + if(!thrdd) return; - thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo); + + async->dns_responses |= CURL_DNSQ_HTTPS; + async->queries_ongoing--; + async->done = !async->queries_ongoing; + if((ARES_SUCCESS == status) && dnsrec) + async->result = Curl_httpsrr_from_ares(dnsrec, &thrdd->rr.hinfo); } -static CURLcode async_rr_start(struct Curl_easy *data, int port) +static CURLcode async_rr_start(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; + struct async_thrdd_ctx *thrdd = &async->thrdd; int status; char *rrname = NULL; DEBUGASSERT(!thrdd->rr.channel); - if(port != 443) { - rrname = curl_maprintf("_%d_.https.%s", port, data->conn->host.name); + if(async->port != 443) { + rrname = curl_maprintf("_%d_.https.%s", async->port, async->hostname); if(!rrname) return CURLE_OUT_OF_MEMORY; } @@ -372,327 +239,82 @@ #endif memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo)); - thrdd->rr.hinfo.port = -1; thrdd->rr.hinfo.rrname = rrname; + async->queries_ongoing++; ares_query_dnsrec(thrdd->rr.channel, - rrname ? rrname : data->conn->host.name, ARES_CLASS_IN, + rrname ? rrname : async->hostname, ARES_CLASS_IN, ARES_REC_TYPE_HTTPS, - async_thrdd_rr_done, data, NULL); - CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name); + async_thrdd_rr_done, async, NULL); + CURL_TRC_DNS(data, "[HTTPS-RR] initiated request for %s", + rrname ? rrname : async->hostname); return CURLE_OK; } #endif -/* - * async_thrdd_init() starts a new thread that performs the actual - * resolve. This function returns before the resolve is done. - * - * Returns FALSE in case of failure, otherwise TRUE. - */ -static bool async_thrdd_init(struct Curl_easy *data, - const char *hostname, int port, int ip_version, - const struct addrinfo *hints) +void Curl_async_thrdd_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr_ctx; + Curl_async_thrdd_destroy(data, async); +} - /* !checksrc! disable ERRNOVAR 1 */ - int err = ENOMEM; +struct async_thrdd_match_ctx { + uint32_t mid; + uint32_t resolv_id; +}; - if(thrdd->addr +static bool async_thrdd_match_item(void *qitem, void *match_data) +{ + const struct async_thrdd_match_ctx *ctx = match_data; + struct async_thrdd_item *item = qitem; + return (item->mid == ctx->mid) && (item->resolv_id == ctx->resolv_id); +} + +void Curl_async_thrdd_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->queries_ongoing && !async->done && + data->multi && data->multi->resolv_thrdq) { + /* Remove any resolve items still queued */ + struct async_thrdd_match_ctx mctx; + mctx.mid = data->mid; + mctx.resolv_id = async->id; + Curl_thrdq_clear(data->multi->resolv_thrdq, + async_thrdd_match_item, &mctx); + } #ifdef USE_HTTPSRR_ARES - || thrdd->rr.channel -#endif - ) { - CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up"); - async_thrdd_destroy(data); - DEBUGASSERT(!thrdd->addr); -#ifdef USE_HTTPSRR_ARES - DEBUGASSERT(!thrdd->rr.channel); -#endif + if(async->thrdd.rr.channel) { + ares_destroy(async->thrdd.rr.channel); + async->thrdd.rr.channel = NULL; } - - data->state.async.dns = NULL; - data->state.async.done = FALSE; - data->state.async.port = port; - data->state.async.ip_version = ip_version; - curlx_free(data->state.async.hostname); - data->state.async.hostname = curlx_strdup(hostname); - if(!data->state.async.hostname) - goto err_exit; - - addr_ctx = addr_ctx_create(data, hostname, port, hints); - if(!addr_ctx) - goto err_exit; - thrdd->addr = addr_ctx; - - /* passing addr_ctx to the thread adds a reference */ - addr_ctx->ref_count = 2; - addr_ctx->start = *Curl_pgrs_now(data); - -#ifdef HAVE_GETADDRINFO - addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx); -#else - addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx); + Curl_httpsrr_cleanup(&async->thrdd.rr.hinfo); #endif - - if(addr_ctx->thread_hnd == curl_thread_t_null) { - /* The thread never started */ - addr_ctx->ref_count = 1; - addr_ctx->thrd_done = TRUE; - err = errno; - goto err_exit; - } - -#ifdef USE_HTTPSRR_ARES - if(async_rr_start(data, port)) - infof(data, "Failed HTTPS RR operation"); -#endif - CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port); - return TRUE; - -err_exit: - CURL_TRC_DNS(data, "resolve thread failed init: %d", err); - async_thrdd_destroy(data); - errno = err; - return FALSE; -} - -static void async_thrdd_shutdown(struct Curl_easy *data) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr; - curl_bit done; - - if(!addr_ctx) - return; - if(addr_ctx->thread_hnd == curl_thread_t_null) - return; - - Curl_mutex_acquire(&addr_ctx->mutx); -#ifndef CURL_DISABLE_SOCKETPAIR - if(!addr_ctx->do_abort) - Curl_multi_will_close(data, addr_ctx->sock_pair[0]); -#endif - addr_ctx->do_abort = TRUE; - done = addr_ctx->thrd_done; - Curl_mutex_release(&addr_ctx->mutx); - - /* Wait for the thread to terminate if it is already marked done. If it is - not done yet we cannot do anything here. We had tried pthread_cancel but - it caused hanging and resource leaks (#18532). */ - if(done && (addr_ctx->thread_hnd != curl_thread_t_null)) { - Curl_thread_join(&addr_ctx->thread_hnd); - CURL_TRC_DNS(data, "async_thrdd_shutdown, thread joined"); - } + async_thrdd_item_destroy(async->thrdd.res_A); + async->thrdd.res_A = NULL; + async_thrdd_item_destroy(async->thrdd.res_AAAA); + async->thrdd.res_AAAA = NULL; } /* - * 'entry' may be NULL and then no data is returned - */ -static CURLcode asyn_thrdd_await(struct Curl_easy *data, - struct async_thrdd_addr_ctx *addr_ctx, - struct Curl_dns_entry **entry) -{ - CURLcode result = CURLE_OK; - - if(addr_ctx->thread_hnd != curl_thread_t_null) { - /* not interested in result? cancel, if still running... */ - if(!entry) - async_thrdd_shutdown(data); - - if(addr_ctx->thread_hnd != curl_thread_t_null) { - CURL_TRC_DNS(data, "resolve, wait for thread to finish"); - if(!Curl_thread_join(&addr_ctx->thread_hnd)) { - DEBUGASSERT(0); - } - } - - if(entry) - result = Curl_async_is_resolved(data, entry); - } - - data->state.async.done = TRUE; - if(entry) - *entry = data->state.async.dns; - - return result; -} - -/* - * Until we gain a way to signal the resolver threads to stop early, we must - * wait for them and ignore their results. - */ -void Curl_async_thrdd_shutdown(struct Curl_easy *data) -{ - async_thrdd_shutdown(data); -} - -void Curl_async_thrdd_destroy(struct Curl_easy *data) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - - if(thrdd->addr && !data->set.quick_exit) { - (void)asyn_thrdd_await(data, thrdd->addr, NULL); - } - async_thrdd_destroy(data); -} - -/* - * Curl_async_await() - * * Waits for a resolve to finish. This function should be avoided since using * this risk getting the multi interface to "hang". - * - * If 'entry' is non-NULL, make it point to the resolved dns entry - * - * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, - * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. - * - * This is the version for resolves-in-a-thread. */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **dns) +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) { - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - if(thrdd->addr) - return asyn_thrdd_await(data, thrdd->addr, dns); - return CURLE_FAILED_INIT; -} + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + struct async_thrdd_ctx *thrdd = async ? &async->thrdd : NULL; + timediff_t milli, ms; -/* - * Curl_async_is_resolved() is called repeatedly to check if a previous - * name resolve request has completed. It should also make sure to time-out if - * the operation seems to take too long. - */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - curl_bit done = FALSE; - - DEBUGASSERT(dns); - *dns = NULL; - - if(data->state.async.done) { - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound", - *dns ? "" : "not "); - return CURLE_OK; - } - -#ifdef USE_HTTPSRR_ARES - /* best effort, ignore errors */ - if(thrdd->rr.channel) - (void)Curl_ares_perform(thrdd->rr.channel, 0); -#endif - - DEBUGASSERT(thrdd->addr); - if(!thrdd->addr) + if(!thrdd) return CURLE_FAILED_INIT; - Curl_mutex_acquire(&thrdd->addr->mutx); - done = thrdd->addr->thrd_done; - Curl_mutex_release(&thrdd->addr->mutx); + while(async->queries_ongoing && !async->done) { + Curl_async_thrdd_multi_process(data->multi); + if(async->done) + break; - if(done) { - CURLcode result = CURLE_OK; - - data->state.async.done = TRUE; - Curl_resolv_unlink(data, &data->state.async.dns); - Curl_expire_done(data, EXPIRE_ASYNC_NAME); - - if(thrdd->addr->res) { - data->state.async.dns = - Curl_dnscache_mk_entry(data, &thrdd->addr->res, - data->state.async.hostname, 0, - data->state.async.port, FALSE); - if(!data->state.async.dns) - result = CURLE_OUT_OF_MEMORY; - -#ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - result = thrdd->rr.result; - if(!result) { - struct Curl_https_rrinfo *lhrr; - lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); - if(!lhrr) - result = CURLE_OUT_OF_MEMORY; - else - data->state.async.dns->hinfo = lhrr; - } - } -#endif - if(!result && data->state.async.dns) - result = Curl_dnscache_add(data, data->state.async.dns); - } - - if(!result && !data->state.async.dns) - result = Curl_resolver_error(data, NULL); - if(result) - Curl_resolv_unlink(data, &data->state.async.dns); - *dns = data->state.async.dns; - CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound", - result, *dns ? "" : "not "); - async_thrdd_shutdown(data); - return result; - } - else { - /* poll for name lookup done with exponential backoff up to 250ms */ - /* should be fine even if this converts to 32-bit */ - timediff_t elapsed = curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle); - if(elapsed < 0) - elapsed = 0; - - if(thrdd->addr->poll_interval == 0) - /* Start at 1ms poll interval */ - thrdd->addr->poll_interval = 1; - else if(elapsed >= thrdd->addr->interval_end) - /* Back-off exponentially if last interval expired */ - thrdd->addr->poll_interval *= 2; - - if(thrdd->addr->poll_interval > 250) - thrdd->addr->poll_interval = 250; - - thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval; - Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME); - return CURLE_OK; - } -} - -CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps) -{ - struct async_thrdd_ctx *thrdd = &data->state.async.thrdd; - CURLcode result = CURLE_OK; - curl_bit thrd_done; - -#if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR) - (void)ps; -#endif - -#ifdef USE_HTTPSRR_ARES - if(thrdd->rr.channel) { - result = Curl_ares_pollset(data, thrdd->rr.channel, ps); - if(result) - return result; - } -#endif - if(!thrdd->addr) - return result; - - Curl_mutex_acquire(&thrdd->addr->mutx); - thrd_done = thrdd->addr->thrd_done; - Curl_mutex_release(&thrdd->addr->mutx); - - if(!thrd_done) { -#ifndef CURL_DISABLE_SOCKETPAIR - /* return read fd to client for polling the DNS resolution status */ - result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]); -#else - timediff_t milli; - timediff_t ms = - curlx_ptimediff_ms(Curl_pgrs_now(data), &thrdd->addr->start); + ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); if(ms < 3) milli = 0; else if(ms <= 50) @@ -701,68 +323,506 @@ milli = 50; else milli = 200; - Curl_expire(data, milli, EXPIRE_ASYNC_NAME); + CURL_TRC_DNS(data, "await, waiting %" FMT_TIMEDIFF_T "ms", milli); + curlx_wait_ms(milli); + } + return Curl_async_take_result(data, async, pdns); +} + +#ifdef HAVE_GETADDRINFO + +/* Process the item, using Curl_getaddrinfo_ex() */ +static void async_thrdd_item_process(void *arg) +{ + struct async_thrdd_item *item = arg; + struct addrinfo hints; + char service[12]; + int pf = PF_INET; + int rc; + +#ifdef DEBUGBUILD + if(item->delay_ms) { + curlx_wait_ms(item->delay_ms); + } + if(item->delay_fail_ms) { + curlx_wait_ms(item->delay_fail_ms); + return; + } #endif + + memset(&hints, 0, sizeof(hints)); +#ifdef CURLRES_IPV6 + if(item->dns_queries & CURL_DNSQ_AAAA) { + pf = (item->dns_queries & CURL_DNSQ_A) ? PF_UNSPEC : PF_INET6; + } +#endif + hints.ai_family = pf; + hints.ai_socktype = Curl_socktype_for_transport(item->transport); + hints.ai_protocol = Curl_protocol_for_transport(item->transport); +#ifdef __APPLE__ + /* If we leave `ai_flags == 0` then macOS is looking for IPV4MAPPED + * when doing AAAA queries. We do not want this "help". */ + hints.ai_flags = AI_ADDRCONFIG; +#endif + + curl_msnprintf(service, sizeof(service), "%u", item->port); +#ifdef AI_NUMERICSERV + hints.ai_flags |= AI_NUMERICSERV; +#endif + + rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res); + if(rc) { + item->sock_error = SOCKERRNO ? SOCKERRNO : rc; + if(item->sock_error == 0) + item->sock_error = RESOLVER_ENOMEM; + } + else { + Curl_addrinfo_set_port(item->res, item->port); + } +} + +#else /* HAVE_GETADDRINFO */ + +/* Process the item, using Curl_ipv4_resolve_r() */ +static void async_thrdd_item_process(void *arg) +{ + struct async_thrdd_item *item = arg; + +#ifdef DEBUGBUILD + if(item->delay_ms) { + curlx_wait_ms(item->delay_ms); + } + if(item->delay_fail_ms) { + curlx_wait_ms(item->delay_fail_ms); + return; + } +#endif + item->res = Curl_ipv4_resolve_r(item->hostname, item->port); + if(!item->res) { + item->sock_error = SOCKERRNO; + if(item->sock_error == 0) + item->sock_error = RESOLVER_ENOMEM; + } +} + +#endif /* HAVE_GETADDRINFO */ + +#ifdef ENABLE_WAKEUP +static void async_thrdd_event(const struct curl_thrdq *tqueue, + Curl_thrdq_event ev, + void *user_data) +{ + struct Curl_multi *multi = user_data; + (void)tqueue; + switch(ev) { + case CURL_THRDQ_EV_ITEM_DONE: + (void)curl_multi_wakeup(multi); + break; + default: + break; + } +} +#else +#define async_thrdd_event NULL +#endif + +static void async_thrdd_item_free(void *item) +{ + async_thrdd_item_destroy(item); +} + +/* Create a thread queue for processing resolv items */ +CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + CURLcode result; + DEBUGASSERT(!multi->resolv_thrdq); + result = Curl_thrdq_create(&multi->resolv_thrdq, "DNS", 0, + min_threads, max_threads, idle_time_ms, + async_thrdd_item_free, + async_thrdd_item_process, + async_thrdd_event, + multi); +#ifdef DEBUGBUILD + if(!result) { + const char *p = getenv("CURL_DBG_RESOLV_MAX_THREADS"); + if(p) { + curl_off_t l; + if(!curlx_str_number(&p, &l, UINT32_MAX)) { + result = Curl_async_thrdd_multi_set_props( + multi, min_threads, (uint32_t)l, idle_time_ms); + } + } + } +#endif + return result; +} + +/* Tear down the thread queue, joining active threads or detaching them */ +void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join) +{ + if(multi->resolv_thrdq) { +#ifdef CURLVERBOSE + CURL_TRC_DNS(multi->admin, "destroy thread queue+pool, join=%d", join); + Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); +#endif + Curl_thrdq_destroy(multi->resolv_thrdq, join); + multi->resolv_thrdq = NULL; + } +} + +#ifdef CURLVERBOSE +static void async_thrdd_report_item(struct Curl_easy *data, + struct async_thrdd_item *item) +{ + char buf[MAX_IPADR_LEN]; + struct dynbuf tmp; + const char *sep = ""; + const struct Curl_addrinfo *ai = item->res; + int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET; + CURLcode result; + + if(!CURL_TRC_DNS_is_verbose(data)) + return; + + curlx_dyn_init(&tmp, 1024); + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + Curl_printable_address(ai, buf, sizeof(buf)); + result = curlx_dyn_addf(&tmp, "%s%s", sep, buf); + if(result) { + CURL_TRC_DNS(data, "too many IP, cannot show"); + goto out; + } + sep = ", "; + } + } + + CURL_TRC_DNS(data, "Host %s:%u resolved IPv%c: %s", + item->hostname, item->port, + (item->dns_queries & CURL_DNSQ_AAAA) ? '6' : '4', + (curlx_dyn_len(&tmp) ? curlx_dyn_ptr(&tmp) : "(none)")); +out: + curlx_dyn_free(&tmp); +} +#endif /* CURLVERBOSE */ + +/* Process the receiving end of the thread queue, dispatching + * processed items to their transfer when it can still be found + * and has an `async` state present. Otherwise, destroy the item. */ +void Curl_async_thrdd_multi_process(struct Curl_multi *multi) +{ + struct Curl_easy *data; + void *qitem; + + while(!Curl_thrdq_recv(multi->resolv_thrdq, &qitem)) { + /* dispatch resolve result */ + struct async_thrdd_item *item = qitem; + struct Curl_resolv_async *async = NULL; + + data = Curl_multi_get_easy(multi, item->mid); + if(data) + async = Curl_async_get(data, item->resolv_id); + if(async) { + struct async_thrdd_item **pdest = &async->thrdd.res_A; + + async->dns_responses |= item->dns_queries; + --async->queries_ongoing; + async->done = !async->queries_ongoing; + +#ifdef CURLRES_IPV6 + if(item->dns_queries & CURL_DNSQ_AAAA) + pdest = &async->thrdd.res_AAAA; +#endif + if(!*pdest) { + VERBOSE(async_thrdd_report_item(data, item)); + *pdest = item; + item = NULL; + } + else + DEBUGASSERT(0); /* should not receive duplicates here */ + Curl_multi_mark_dirty(data); + } + async_thrdd_item_free(item); + } +#ifdef CURLVERBOSE + Curl_thrdq_trace(multi->resolv_thrdq, multi->admin); +#endif +} + +CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + return Curl_thrdq_set_props(multi->resolv_thrdq, 0, + min_threads, max_threads, idle_time_ms); +} + +static CURLcode async_thrdd_query(struct Curl_easy *data, + struct Curl_resolv_async *async, + uint8_t dns_queries) +{ + struct async_thrdd_item *item; + CURLcode result; + + item = async_thrdd_item_create(data, async->id, dns_queries, + async->hostname, async->port, + async->transport); + if(!item) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + CURL_TRC_DNS(data, "queueing query %s", item->description); + result = Curl_thrdq_send(data->multi->resolv_thrdq, item, + async_item_description(item), async->timeout_ms); + if(result) + goto out; + item = NULL; + async->queries_ongoing++; + +out: + if(item) + async_thrdd_item_free(item); + return result; +} + +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + CURLcode result = CURLE_FAILED_INIT; + void *resolver = NULL; + + if(async->queries_ongoing || async->done) + return CURLE_FAILED_INIT; + +#ifdef USE_HTTPSRR_ARES + DEBUGASSERT(!async->thrdd.rr.channel); + if((async->dns_queries & CURL_DNSQ_HTTPS) && !async->is_ipaddr) { + result = async_rr_start(data, async); + if(result) + goto out; + resolver = async->thrdd.rr.channel; + } +#endif + + result = Curl_resolv_announce_start(data, resolver); + if(result) + return result; + +#ifdef CURLRES_IPV6 + /* Do not start an AAAA query for an ipv4 address when + * we will start an A query for it. */ + if((async->dns_queries & CURL_DNSQ_AAAA) && + !(async->is_ipv4addr && (async->dns_queries & CURL_DNSQ_A))) { + result = async_thrdd_query(data, async, CURL_DNSQ_AAAA); + if(result) + goto out; + } +#endif + if(async->dns_queries & CURL_DNSQ_A) { + result = async_thrdd_query(data, async, CURL_DNSQ_A); + if(result) + goto out; + } + +#ifdef CURLVERBOSE + Curl_thrdq_trace(data->multi->resolv_thrdq, data); +#endif + +out: + if(result) + CURL_TRC_DNS(data, "error queueing query %s:%d -> %d", + async->hostname, async->port, result); + return result; +} + +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps) +{ + timediff_t timeout_ms; + + timeout_ms = Curl_async_timeleft_ms(data, async); +#ifdef USE_HTTPSRR_ARES + if(async->thrdd.rr.channel) { + CURLcode result = Curl_ares_pollset(data, async->thrdd.rr.channel, ps); + if(result) + return result; + timeout_ms = Curl_ares_timeout_ms(data, async, async->thrdd.rr.channel); + } +#else + (void)ps; +#endif + + if(!async->done) { +#ifndef ENABLE_WAKEUP + timediff_t stutter_ms, elapsed_ms; + elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); + if(elapsed_ms < 3) + stutter_ms = 1; + else if(elapsed_ms <= 50) + stutter_ms = elapsed_ms / 3; + else if(elapsed_ms <= 250) + stutter_ms = 50; + else + stutter_ms = 200; + timeout_ms = CURLMIN(stutter_ms, timeout_ms); +#endif + Curl_expire(data, timeout_ms, EXPIRE_ASYNC_NAME); + } + return CURLE_OK; +} + +/* + * Curl_async_take_result() is called repeatedly to check if a previous + * name resolve request has completed. It should also make sure to time-out if + * the operation seems to take too long. + */ +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) +{ + struct async_thrdd_ctx *thrdd = &async->thrdd; + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; + + DEBUGASSERT(pdns); + *pdns = NULL; + if(!async->queries_ongoing && !async->done) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + +#ifdef USE_HTTPSRR_ARES + /* best effort, ignore errors */ + if(thrdd->rr.channel) + (void)Curl_ares_perform(thrdd->rr.channel, 0); +#endif + + if(!async->done) + return CURLE_AGAIN; + + Curl_expire_done(data, EXPIRE_ASYNC_NAME); + if(async->result) { + result = async->result; + goto out; + } + + if((thrdd->res_A && thrdd->res_A->res) || + (thrdd->res_AAAA && thrdd->res_AAAA->res)) { + dns = Curl_dnscache_mk_entry2( + data, async->dns_queries, + thrdd->res_A ? &thrdd->res_A->res : NULL, + thrdd->res_AAAA ? &thrdd->res_AAAA->res : NULL, + async->hostname, async->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + +#ifdef USE_HTTPSRR_ARES + if(thrdd->rr.channel) { + struct Curl_https_rrinfo *lhrr = NULL; + if(thrdd->rr.hinfo.complete) { + lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo); + if(!lhrr) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + Curl_httpsrr_trace(data, lhrr); + Curl_dns_entry_set_https_rr(dns, lhrr); + } +#endif + } + + if(dns) { + *pdns = dns; + dns = NULL; + } +#ifdef CURLVERBOSE + Curl_thrdq_trace(data->multi->resolv_thrdq, data); +#endif + +out: + Curl_dns_entry_unlink(data, &dns); + Curl_async_thrdd_shutdown(data, async); + if(!result && !*pdns) + result = Curl_async_failed(data, async, NULL); + if(result && + (result != CURLE_COULDNT_RESOLVE_HOST) && + (result != CURLE_COULDNT_RESOLVE_PROXY)) { + CURL_TRC_DNS(data, "Error %d resolving %s:%d", + result, async->hostname, async->port); } return result; } -#ifndef HAVE_GETADDRINFO -/* - * Curl_async_getaddrinfo() - for platforms without getaddrinfo - */ -CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, - int port, int ip_version) +static const struct Curl_addrinfo *async_thrdd_get_ai( + const struct Curl_addrinfo *ai, + int ai_family, unsigned int index) { - (void)ip_version; - - /* fire up a new resolver thread! */ - if(async_thrdd_init(data, hostname, port, ip_version, NULL)) { - return CURLE_OK; + unsigned int i = 0; + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } } - - failf(data, "getaddrinfo() thread failed"); - return CURLE_FAILED_INIT; + return NULL; } -#else /* !HAVE_GETADDRINFO */ - -/* - * Curl_async_getaddrinfo() - for getaddrinfo - */ -CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, - int port, int ip_version) +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index) { - struct addrinfo hints; - int pf = PF_INET; + struct async_thrdd_ctx *thrdd = &async->thrdd; - CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port); -#ifdef CURLRES_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { - /* The stack seems to be IPv6-enabled */ - if(ip_version == CURL_IPRESOLVE_V6) - pf = PF_INET6; - else - pf = PF_UNSPEC; + (void)data; + switch(ai_family) { + case AF_INET: + if(thrdd->res_A) + return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index); + break; + case AF_INET6: + if(thrdd->res_AAAA) + return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index); + break; + default: + break; } + return NULL; +} + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async) +{ +#ifdef USE_HTTPSRR_ARES + if(Curl_async_knows_https(data, async)) + return &async->thrdd.rr.hinfo; #else - (void)ip_version; -#endif /* CURLRES_IPV6 */ - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? - SOCK_STREAM : SOCK_DGRAM; - - /* fire up a new resolver thread! */ - if(async_thrdd_init(data, hostname, port, ip_version, &hints)) - return CURLE_OK; - - failf(data, "getaddrinfo() thread failed to start"); - return CURLE_FAILED_INIT; + (void)data; + (void)async; +#endif + return NULL; } -#endif /* !HAVE_GETADDRINFO */ +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async) +{ + (void)data; + if(async->dns_queries & CURL_DNSQ_HTTPS) + return ((async->dns_responses & CURL_DNSQ_HTTPS) || async->done); + return TRUE; /* we know it will never come */ +} -#endif /* CURLRES_THREADED */ +#endif /* USE_HTTPSRR */ + +#endif /* USE_RESOLV_THREADED */
diff --git a/Utilities/cmcurl/lib/asyn.h b/Utilities/cmcurl/lib/asyn.h index 68628f9..ed50933 100644 --- a/Utilities/cmcurl/lib/asyn.h +++ b/Utilities/cmcurl/lib/asyn.h
@@ -31,6 +31,9 @@ struct Curl_easy; struct Curl_dns_entry; +struct Curl_resolv_async; +struct Curl_multi; +struct easy_pollset; #ifdef CURLRES_ASYNCH @@ -40,8 +43,8 @@ struct connectdata; struct easy_pollset; -#if defined(CURLRES_ARES) && defined(CURLRES_THREADED) -#error cannot have both CURLRES_ARES and CURLRES_THREADED defined +#if defined(USE_RESOLV_ARES) && defined(USE_RESOLV_THREADED) +#error cannot have both USE_RESOLV_ARES and USE_RESOLV_THREADED defined #endif /* @@ -66,49 +69,6 @@ void Curl_async_global_cleanup(void); /* - * Curl_async_get_impl() - * Get the resolver implementation instance (c-ares channel) or NULL - * for passing to application callback. - */ -CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl); - -/* Curl_async_pollset() - * - * This function is called from the Curl_multi_pollset() function. 'sock' is a - * pointer to an array to hold the file descriptors, with 'numsock' being the - * size of that array (in number of entries). This function is supposed to - * return bitmask indicating what file descriptors (referring to array indexes - * in the 'sock' array) to wait for, read/write. - */ -CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps); - -/* - * Curl_async_is_resolved() - * - * Called repeatedly to check if a previous name resolve request has - * completed. It should also make sure to time-out if the operation seems to - * take too long. - * - * Returns normal CURLcode errors. - */ -CURLcode Curl_async_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dns); - -/* - * Curl_async_await() - * - * Waits for a resolve to finish. This function should be avoided since using - * this risk getting the multi interface to "hang". - * - * On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise. - * - * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, - * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. - */ -CURLcode Curl_async_await(struct Curl_easy *data, - struct Curl_dns_entry **dns); - -/* * Curl_async_getaddrinfo() - when using this resolver * * Returns name information about the given hostname and port number. If @@ -119,8 +79,21 @@ * Each resolver backend must of course make sure to return data in the * correct format to comply with this. */ -CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, const char *hostname, - int port, int ip_version); +CURLcode Curl_async_getaddrinfo(struct Curl_easy *data, + struct Curl_resolv_async *async); + +const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data, + struct Curl_resolv_async *async, + int ai_family, + unsigned int index); + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_async_get_https( + struct Curl_easy *data, + struct Curl_resolv_async *async); +bool Curl_async_knows_https(struct Curl_easy *data, + struct Curl_resolv_async *async); +#endif /* USE_HTTPSRR */ #ifdef USE_ARES /* common functions for c-ares and threaded resolver with HTTPSRR */ @@ -130,105 +103,119 @@ ares_channel channel, struct easy_pollset *ps); +timediff_t Curl_ares_timeout_ms(struct Curl_easy *data, + struct Curl_resolv_async *async, + ares_channel channel); + int Curl_ares_perform(ares_channel channel, timediff_t timeout_ms); #endif -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES /* async resolving implementation using c-ares alone */ struct async_ares_ctx { ares_channel channel; - int num_pending; /* number of outstanding c-ares requests */ - struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares - parts */ + struct Curl_addrinfo *res_A; + struct Curl_addrinfo *res_AAAA; int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */ CURLcode result; /* CURLE_OK or error handling response */ -#ifndef HAVE_CARES_GETADDRINFO struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */ -#endif #ifdef USE_HTTPSRR struct Curl_https_rrinfo hinfo; #endif }; -void Curl_async_ares_shutdown(struct Curl_easy *data); -void Curl_async_ares_destroy(struct Curl_easy *data); +void Curl_async_ares_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); +void Curl_async_ares_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); -/* Set the DNS server to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data); +#endif /* USE_RESOLV_ARES */ -/* Set the DNS interfacer to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data); +#ifdef USE_RESOLV_THREADED -/* Set the local ipv4 address to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data); - -/* Set the local ipv6 address to use by ares, from `data` settings. */ -CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data); - -#endif /* CURLRES_ARES */ - -#ifdef CURLRES_THREADED -/* async resolving implementation using POSIX threads */ -#include "curl_threads.h" - -/* Context for threaded address resolver */ -struct async_thrdd_addr_ctx { - curl_thread_t thread_hnd; - char *hostname; /* hostname to resolve, Curl_async.hostname - duplicate */ - curl_mutex_t mutx; -#ifndef CURL_DISABLE_SOCKETPAIR - curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */ -#endif - struct Curl_addrinfo *res; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints; -#endif - struct curltime start; - timediff_t interval_end; - unsigned int poll_interval; - int port; - int sock_error; - int ref_count; - BIT(thrd_done); - BIT(do_abort); -}; +struct async_thrdd_item; /* Context for threaded resolver */ struct async_thrdd_ctx { - /* `addr` is a pointer since this memory is shared with a started - * thread. Since threads cannot be killed, we use reference counting - * so that we can "release" our pointer to this memory while the - * thread is still running. */ - struct async_thrdd_addr_ctx *addr; + struct async_thrdd_item *res_A; /* ipv4 result */ + struct async_thrdd_item *res_AAAA; /* ipv6 result */ #if defined(USE_HTTPSRR) && defined(USE_ARES) struct { ares_channel channel; struct Curl_https_rrinfo hinfo; - CURLcode result; - BIT(done); } rr; #endif }; -void Curl_async_thrdd_shutdown(struct Curl_easy *data); -void Curl_async_thrdd_destroy(struct Curl_easy *data); +void Curl_async_thrdd_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); +void Curl_async_thrdd_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); -#endif /* CURLRES_THREADED */ +CURLcode Curl_async_thrdd_multi_init(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); +void Curl_async_thrdd_multi_destroy(struct Curl_multi *multi, bool join); +void Curl_async_thrdd_multi_process(struct Curl_multi *multi); + +CURLcode Curl_async_thrdd_multi_set_props(struct Curl_multi *multi, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#endif /* USE_RESOLV_THREADED */ #ifndef CURL_DISABLE_DOH struct doh_probes; #endif +/* + * Curl_async_await() + * + * Waits for a resolve to finish. This function should be avoided since using + * this risk getting the multi interface to "hang". + * + * On return 'dns' is assigned the resolved dns (CURLE_OK or NULL otherwise. + * + * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, + * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. + */ +CURLcode Curl_async_await(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns); + +/* + * Take the result of an async resolve operation. + * Returns CURLE_OK with `*pdns` != NULL, CURLE_AGAIN while still + * ongoing or an error code for a failed resolve. + */ +CURLcode Curl_async_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns); + +/* Curl_async_pollset() + * + * This function is called from the Curl_multi_pollset() function. 'sock' is a + * pointer to an array to hold the file descriptors, with 'numsock' being the + * size of that array (in number of entries). This function is supposed to + * return bitmask indicating what file descriptors (referring to array indexes + * in the 'sock' array) to wait for, read/write. + */ +CURLcode Curl_async_pollset(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct easy_pollset *ps); + #else /* CURLRES_ASYNCH */ /* convert these functions if an asynch resolver is not used */ -#define Curl_async_get_impl(x, y) (*(y) = NULL, CURLE_OK) -#define Curl_async_is_resolved(x, y) CURLE_COULDNT_RESOLVE_HOST -#define Curl_async_await(x, y) CURLE_COULDNT_RESOLVE_HOST -#define Curl_async_global_init() CURLE_OK -#define Curl_async_global_cleanup() Curl_nop_stmt - +#define Curl_async_global_init() CURLE_OK +#define Curl_async_global_cleanup() Curl_nop_stmt +#define Curl_async_get_ai(a, b, c, d) NULL +#define Curl_async_await(a, b, c) CURLE_COULDNT_RESOLVE_HOST +#define Curl_async_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST +#define Curl_async_pollset(x, y, z) CURLE_OK +#define Curl_async_get_https(x, y) NULL +#define Curl_async_knows_https(x, y) TRUE #endif /* !CURLRES_ASYNCH */ #if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH) @@ -236,38 +223,54 @@ #endif #ifdef USE_CURL_ASYNC -struct Curl_async { -#ifdef CURLRES_ARES + +struct Curl_resolv_async { + struct Curl_resolv_async *next; +#ifdef USE_RESOLV_ARES struct async_ares_ctx ares; -#elif defined(CURLRES_THREADED) +#elif defined(USE_RESOLV_THREADED) struct async_thrdd_ctx thrdd; #endif #ifndef CURL_DISABLE_DOH struct doh_probes *doh; /* DoH specific data for this request */ #endif - struct Curl_dns_entry *dns; /* result of resolving on success */ - char *hostname; /* copy of the params resolv started with */ - int port; - int ip_version; + struct curltime start; + timediff_t interval_end; + timediff_t timeout_ms; + CURLcode result; + uint32_t poll_interval; + uint32_t id; /* unique id per easy handle of the resolve operation */ + /* what is being resolved */ + uint16_t port; + uint8_t dns_queries; /* what queries are being performed */ + uint8_t dns_responses; /* what queries had responses so far. */ + uint8_t transport; + uint8_t queries_ongoing; + BIT(is_ipaddr); + BIT(is_ipv4addr); + BIT(for_proxy); BIT(done); + BIT(shutdown); + char hostname[1]; }; -/* - * Curl_async_shutdown(). - * - * This shuts down all ongoing operations. - */ -void Curl_async_shutdown(struct Curl_easy *data); +timediff_t Curl_async_timeleft_ms(struct Curl_easy *data, + struct Curl_resolv_async *async); -/* - * Curl_async_destroy(). - * - * This frees the resources of any async resolve. - */ -void Curl_async_destroy(struct Curl_easy *data); +/* Shut down the given async resolve. */ +void Curl_async_shutdown(struct Curl_easy *data, + struct Curl_resolv_async *async); + +/* Frees the resources of the given async resolve and the struct itself. */ +void Curl_async_destroy(struct Curl_easy *data, + struct Curl_resolv_async *async); + +CURLcode Curl_async_failed(struct Curl_easy *data, + struct Curl_resolv_async *async, + const char *detail); + #else /* !USE_CURL_ASYNC */ -#define Curl_async_shutdown(x) Curl_nop_stmt -#define Curl_async_destroy(x) Curl_nop_stmt +#define Curl_async_shutdown(x, y) Curl_nop_stmt #endif /* USE_CURL_ASYNC */ /********** end of generic resolver interface functions *****************/
diff --git a/Utilities/cmcurl/lib/bufq.h b/Utilities/cmcurl/lib/bufq.h index 8dd186f..da411b5 100644 --- a/Utilities/cmcurl/lib/bufq.h +++ b/Utilities/cmcurl/lib/bufq.h
@@ -132,8 +132,8 @@ size_t max_chunks, int opts); /** - * Reset the buffer queue to be empty. Will keep any allocated buffer - * chunks around. + * Reset the buffer queue to be empty. Keep any allocated buffer chunks + * around. */ void Curl_bufq_reset(struct bufq *q); @@ -243,7 +243,7 @@ /** * Write buf to the end of the buffer queue. - * Will write bufq content or passed `buf` directly using the `writer` + * Write bufq content or passed `buf` directly using the `writer` * callback when it sees fit. 'buf' might get passed directly * on or is placed into the buffer, depending on `len` and current * amount buffered, chunk size, etc.
diff --git a/Utilities/cmcurl/lib/cf-dns.c b/Utilities/cmcurl/lib/cf-dns.c new file mode 100644 index 0000000..e763b8e --- /dev/null +++ b/Utilities/cmcurl/lib/cf-dns.c
@@ -0,0 +1,667 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "urldata.h" +#include "curl_addrinfo.h" +#include "cfilters.h" +#include "connect.h" +#include "dnscache.h" +#include "httpsrr.h" +#include "curl_trc.h" +#include "progress.h" +#include "url.h" +#include "cf-dns.h" + + +struct cf_dns_ctx { + struct Curl_dns_entry *dns; + CURLcode resolv_result; + uint32_t resolv_id; + uint16_t port; + uint8_t dns_queries; + uint8_t transport; + BIT(started); + BIT(announced); + BIT(abstract_unix_socket); + BIT(complete_resolve); + BIT(for_proxy); + char hostname[1]; +}; + +static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, uint8_t transport, + bool abstract_unix_socket, + bool for_proxy, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct cf_dns_ctx *ctx; + size_t hlen = strlen(hostname); + + ctx = curlx_calloc(1, sizeof(*ctx) + hlen); + if(!ctx) + return NULL; + + ctx->port = port; + ctx->dns_queries = dns_queries; + ctx->transport = transport; + ctx->abstract_unix_socket = abstract_unix_socket; + ctx->for_proxy = for_proxy; + ctx->complete_resolve = complete_resolve; + ctx->dns = Curl_dns_entry_link(data, dns); + ctx->started = !!ctx->dns; + if(hlen) + memcpy(ctx->hostname, hostname, hlen); + + CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x", + ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries); + return ctx; +} + +static void cf_dns_ctx_destroy(struct Curl_easy *data, + struct cf_dns_ctx *ctx) +{ + if(ctx) { + Curl_dns_entry_unlink(data, &ctx->dns); + curlx_free(ctx); + } +} + +#ifdef CURLVERBOSE +static void cf_dns_report_addr(struct Curl_easy *data, + struct dynbuf *tmp, + const char *label, + int ai_family, + const struct Curl_addrinfo *ai) +{ + char buf[MAX_IPADR_LEN]; + const char *sep = ""; + CURLcode result; + + curlx_dyn_reset(tmp); + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + Curl_printable_address(ai, buf, sizeof(buf)); + result = curlx_dyn_addf(tmp, "%s%s", sep, buf); + if(result) { + infof(data, "too many IP, cannot show"); + return; + } + sep = ", "; + } + } + + infof(data, "%s%s", label, + (curlx_dyn_len(tmp) ? curlx_dyn_ptr(tmp) : "(none)")); +} + +static void cf_dns_report(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + struct cf_dns_ctx *ctx = cf->ctx; + struct dynbuf tmp; + + if(!Curl_trc_is_verbose(data) || + /* ignore no name or numerical IP addresses */ + !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) + return; + + switch(ctx->transport) { + case TRNSPRT_UNIX: +#ifdef USE_UNIX_SOCKETS + CURL_TRC_CF(data, cf, "resolved unix domain %s", + Curl_conn_get_unix_path(data->conn)); +#else + DEBUGASSERT(0); +#endif + break; + default: + curlx_dyn_init(&tmp, 1024); + infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port); +#ifdef CURLRES_IPV6 + cf_dns_report_addr(data, &tmp, "IPv6: ", AF_INET6, dns->addr); +#endif + cf_dns_report_addr(data, &tmp, "IPv4: ", AF_INET, dns->addr); +#ifdef USE_HTTPSRR + if(!dns->hinfo) + infof(data, "HTTPS-RR: -"); + else if(!Curl_httpsrr_applicable(data, dns->hinfo)) + infof(data, "HTTPS-RR: not applicable"); + else { + CURLcode result = Curl_httpsrr_print(&tmp, dns->hinfo); + if(!result) + infof(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); + else + infof(data, "Error printing HTTPS-RR information"); + } +#endif + curlx_dyn_free(&tmp); + break; + } +} +#else +#define cf_dns_report(x, y, z) Curl_nop_stmt +#endif + +/************************************************************* + * Resolve the address of the server or proxy + *************************************************************/ +static CURLcode cf_dns_start(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct Curl_dns_entry **pdns) +{ + struct cf_dns_ctx *ctx = cf->ctx; + timediff_t timeout_ms = Curl_timeleft_ms(data); + CURLcode result; + + *pdns = NULL; + +#ifdef USE_UNIX_SOCKETS + if(ctx->transport == TRNSPRT_UNIX) { + CURL_TRC_CF(data, cf, "resolve unix socket %s", ctx->hostname); + return Curl_resolv_unix(data, ctx->hostname, + (bool)cf->conn->bits.abstract_unix_socket, pdns); + } +#endif + + /* Resolve target host right on */ + CURL_TRC_CF(data, cf, "cf_dns_start host %s:%u", ctx->hostname, ctx->port); + if(Curl_is_ipv4addr(ctx->hostname)) + ctx->dns_queries |= CURL_DNSQ_A; +#ifdef USE_IPV6 + else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */ + ctx->dns_queries |= CURL_DNSQ_AAAA; +#endif + result = Curl_resolv(data, ctx->dns_queries, + ctx->hostname, ctx->port, ctx->transport, + (bool)ctx->for_proxy, timeout_ms, + &ctx->resolv_id, pdns); + DEBUGASSERT(!result || !*pdns); + if(!result) { /* resolved right away, either sync or from dnscache */ + DEBUGASSERT(*pdns); + return CURLE_OK; + } + else if(result == CURLE_AGAIN) { /* async resolv in progress */ + return CURLE_OK; + } + else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */ + failf(data, "Failed to resolve '%s' with timeout after %" + FMT_TIMEDIFF_T " ms", ctx->hostname, + curlx_ptimediff_ms(Curl_pgrs_now(data), + &data->progress.t_startsingle)); + return CURLE_OPERATION_TIMEDOUT; + } + else { + DEBUGASSERT(result); + failf(data, "Could not resolve: %s", ctx->hostname); + return result; + } +} + +#define CURL_HEV3_RESOLVE_DELAY_MS 50 + +static bool cf_dns_ready_to_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_dns_ctx *ctx = cf->ctx; + + if(ctx->resolv_result) + return TRUE; + else if(ctx->dns) + return TRUE; +#ifdef USE_CURL_ASYNC + else { + /* We want AAAA answer as we prefer ipv6. If a sub-filter desires + * HTTPS-RR, we check for that query as well. */ + uint8_t wanted_answers = CURL_DNSQ_AAAA; + if(Curl_conn_cf_wants_httpsrr(cf, data)) + wanted_answers |= CURL_DNSQ_HTTPS; + + /* Note: if a query was never started, it is considered to have + * an answer (e.g. a negative one). */ + if(Curl_resolv_has_answers(data, ctx->resolv_id, wanted_answers)) + return TRUE; + /* If the wanted answers are not available after a delay, + * we let the connect attempts start anyway. */ + return Curl_resolv_elapsed_ms(data, ctx->resolv_id) >= + CURL_HEV3_RESOLVE_DELAY_MS; + } +#else + (void)data; + DEBUGASSERT(0); /* We should not come here */ + return FALSE; +#endif /* USE_CURL_ASYNC */ +} + +static CURLcode cf_dns_connect(struct Curl_cfilter *cf, + struct Curl_easy *data, + bool *done) +{ + struct cf_dns_ctx *ctx = cf->ctx; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + *done = FALSE; + if(!ctx->started) { + ctx->started = TRUE; + ctx->resolv_result = cf_dns_start(cf, data, &ctx->dns); + } + + if(!ctx->dns && !ctx->resolv_result) { + ctx->resolv_result = + Curl_resolv_take_result(data, ctx->resolv_id, &ctx->dns); + } + + if(ctx->resolv_result) { + CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result); + return ctx->resolv_result; + } + + if(ctx->dns && !ctx->announced) { + ctx->announced = TRUE; + if(cf->sockindex == FIRSTSOCKET) { + cf->conn->bits.dns_resolved = TRUE; + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + } + cf_dns_report(cf, data, ctx->dns); + } + + if(!cf_dns_ready_to_connect(cf, data)) { + return CURLE_OK; + } + + if(cf->next && !cf->next->connected) { + bool sub_done; + CURLcode result = Curl_conn_cf_connect(cf->next, data, &sub_done); + if(result || !sub_done) + return result; + DEBUGASSERT(sub_done); + } + + /* sub filter chain is connected */ + CURL_TRC_CF(data, cf, "connected filter chain below"); + if(ctx->complete_resolve && !ctx->dns && !ctx->resolv_result) { + /* This filter only connects when it has resolved everything. */ + CURL_TRC_CF(data, cf, "delay connect until resolve complete"); + return CURLE_OK; + } + *done = TRUE; + cf->connected = TRUE; + Curl_resolv_destroy(data, ctx->resolv_id); + return CURLE_OK; +} + +static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + struct cf_dns_ctx *ctx = cf->ctx; + + CURL_TRC_CF(data, cf, "destroy"); + cf_dns_ctx_destroy(data, ctx); +} + +static void cf_dns_close(struct Curl_cfilter *cf, struct Curl_easy *data) +{ + cf->connected = FALSE; + if(cf->next) + cf->next->cft->do_close(cf->next, data); +} + +static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct easy_pollset *ps) +{ +#ifdef USE_CURL_ASYNC + if(!cf->connected) + return Curl_resolv_pollset(data, ps); +#else + (void)cf; + (void)data; + (void)ps; +#endif + return CURLE_OK; +} + +static CURLcode cf_dns_cntrl(struct Curl_cfilter *cf, + struct Curl_easy *data, + int event, int arg1, void *arg2) +{ + struct cf_dns_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)arg1; + (void)arg2; + switch(event) { + case CF_CTRL_DATA_DONE: + if(ctx->dns) { + /* Should only come here when the connect attempt failed and + * `data` is giving up on it. On a successful connect, we already + * unlinked the DNS entry. */ + Curl_dns_entry_unlink(data, &ctx->dns); + } + break; + default: + break; + } + return result; +} + +struct Curl_cftype Curl_cft_dns = { + "DNS", + CF_TYPE_SETUP, + CURL_LOG_LVL_NONE, + cf_dns_destroy, + cf_dns_connect, + cf_dns_close, + Curl_cf_def_shutdown, + cf_dns_adjust_pollset, + Curl_cf_def_data_pending, + Curl_cf_def_send, + Curl_cf_def_recv, + cf_dns_cntrl, + Curl_cf_def_conn_is_alive, + Curl_cf_def_conn_keep_alive, + Curl_cf_def_query, +}; + +static CURLcode cf_dns_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool abstract_unix_socket, + bool for_proxy, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct Curl_cfilter *cf = NULL; + struct cf_dns_ctx *ctx; + CURLcode result = CURLE_OK; + + (void)data; + ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport, + abstract_unix_socket, for_proxy, + complete_resolve, dns); + if(!ctx) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = Curl_cf_create(&cf, &Curl_cft_dns, ctx); + +out: + *pcf = result ? NULL : cf; + if(result) + cf_dns_ctx_destroy(data, ctx); + return result; +} + +/* Create a "resolv" filter for the transfer's connection. Figures + * out the hostname/path and port where to connect to. */ +static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf, + struct Curl_easy *data, + uint8_t dns_queries, + uint8_t transport, + bool complete_resolve, + struct Curl_dns_entry *dns) +{ + struct connectdata *conn = data->conn; + const char *hostname = NULL; + uint16_t port = 0; + bool abstract_unix_socket = FALSE, for_proxy = FALSE; + +#ifdef USE_UNIX_SOCKETS + { + const char *unix_path = Curl_conn_get_unix_path(conn); + if(unix_path) { + DEBUGASSERT(transport == TRNSPRT_UNIX); + hostname = unix_path; + abstract_unix_socket = (bool)conn->bits.abstract_unix_socket; + } + } +#endif + +#ifndef CURL_DISABLE_PROXY + if(!hostname && conn->bits.proxy) { + for_proxy = TRUE; + hostname = conn->bits.socksproxy ? + conn->socks_proxy.host.name : conn->http_proxy.host.name; + port = conn->bits.socksproxy ? + conn->socks_proxy.port : conn->http_proxy.port; + } +#endif + if(!hostname) { + struct hostname *ehost; + ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; + /* If not connecting via a proxy, extract the port from the URL, if it is + * there, thus overriding any defaults that might have been set above. */ + hostname = ehost->name; + port = conn->bits.conn_to_port ? + conn->conn_to_port : (uint16_t)conn->remote_port; + } + + if(!hostname) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + return cf_dns_create(pcf, data, dns_queries, + hostname, port, transport, + abstract_unix_socket, for_proxy, + complete_resolve, dns); +} + +/* Adds a "resolv" filter at the top of the connection's filter chain. + * For FIRSTSOCKET, the `dns` parameter may be NULL. The filter will + * figure out hostname and port to connect to and start the DNS resolve + * on the first connect attempt. + * For SECONDARYSOCKET, the `dns` parameter must be given. + */ +CURLcode Curl_cf_dns_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t dns_queries, + uint8_t transport, + struct Curl_dns_entry *dns) +{ + struct Curl_cfilter *cf = NULL; + CURLcode result; + + DEBUGASSERT(data); + if(sockindex == FIRSTSOCKET) + result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns); + else if(dns) { + result = cf_dns_create(&cf, data, dns_queries, + dns->hostname, dns->port, transport, + FALSE, FALSE, FALSE, dns); + } + else { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + } + if(result) + goto out; + Curl_conn_cf_add(data, conn, sockindex, cf); +out: + return result; +} + +/* Insert a new "resolv" filter directly after `cf`. It will + * start a DNS resolve for the given hostnmae and port on the + * first connect attempt. + * See socks.c on how this is used to make a non-blocking DNS + * resolve during connect. + */ +CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool complete_resolve) +{ + struct Curl_cfilter *cf; + CURLcode result; + + result = cf_dns_create(&cf, data, dns_queries, + hostname, port, transport, + FALSE, FALSE, complete_resolve, NULL); + if(result) + return result; + + Curl_conn_cf_insert_after(cf_at, cf); + return CURLE_OK; +} + +/* Return the resolv result from the first "resolv" filter, starting + * the given filter `cf` downwards. + */ +static CURLcode cf_dns_result(struct Curl_cfilter *cf) +{ + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns || ctx->resolv_result) + return ctx->resolv_result; + return CURLE_AGAIN; + } + } + return CURLE_FAILED_INIT; +} + +/* Return the result of the DNS resolution. Searches for a "resolv" + * filter from the top of the filter chain down. Returns + * - CURLE_AGAIN when not done yet + * - CURLE_OK when DNS was successfully resolved + * - CURLR_FAILED_INIT when no resolv filter was found + * - error returned by the DNS resolv + */ +CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex) +{ + return cf_dns_result(conn->cfilter[sockindex]); +} + +static const struct Curl_addrinfo *cf_dns_get_nth_ai( + struct Curl_cfilter *cf, + const struct Curl_addrinfo *ai, + int ai_family, unsigned int index) +{ + struct cf_dns_ctx *ctx = cf->ctx; + unsigned int i = 0; + + if((ai_family == AF_INET) && !(ctx->dns_queries & CURL_DNSQ_A)) + return NULL; +#ifdef USE_IPV6 + if((ai_family == AF_INET6) && !(ctx->dns_queries & CURL_DNSQ_AAAA)) + return NULL; +#endif + for(i = 0; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) { + if(i == index) + return ai; + ++i; + } + } + return NULL; +} + +/* Return the addrinfo at `index` for the given `family` from the + * first "resolve" filter underneath `cf`. If the DNS resolving is + * not done yet or if no address for the family exists, returns NULL. + */ +const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, + struct Curl_easy *data, + int ai_family, + unsigned int index) +{ + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->resolv_result) + return NULL; + else if(ctx->dns) + return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index); + else + return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index); + } + } + return NULL; +} + +/* Return the addrinfo at `index` for the given `family` from the + * first "resolve" filter at the connection. If the DNS resolving is + * not done yet or if no address for the family exists, returns NULL. + */ +const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + int sockindex, int ai_family, + unsigned int index) +{ + struct connectdata *conn = data->conn; + return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, ai_family, index); +} + +#ifdef USE_HTTPSRR +/* Return the HTTPS-RR info from the first "resolve" filter at the + * connection. If the DNS resolving is not done yet or if there + * is no HTTPS-RR info, returns NULL. + */ +const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex) +{ + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns) + return ctx->dns->hinfo; + else + return Curl_resolv_get_https(data, ctx->resolv_id); + } + } + return NULL; +} + +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + for(; cf; cf = cf->next) { + if(cf->cft == &Curl_cft_dns) { + struct cf_dns_ctx *ctx = cf->ctx; + if(ctx->dns) + return TRUE; + else + return Curl_resolv_knows_https(data, ctx->resolv_id); + } + } + return FALSE; +} + +#endif /* USE_HTTPSRR */
diff --git a/Utilities/cmcurl/lib/cf-dns.h b/Utilities/cmcurl/lib/cf-dns.h new file mode 100644 index 0000000..3c46b1b --- /dev/null +++ b/Utilities/cmcurl/lib/cf-dns.h
@@ -0,0 +1,71 @@ +#ifndef HEADER_CURL_CF_DNS_H +#define HEADER_CURL_CF_DNS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +struct Curl_easy; +struct connectdata; +struct Curl_dns_entry; +struct Curl_addrinfo; + +CURLcode Curl_cf_dns_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t dns_queries, + uint8_t transport, + struct Curl_dns_entry *dns); + +CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at, + struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool complete_resolve); + +CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex); + +const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data, + int sockindex, + int ai_family, + unsigned int index); + +const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf, + struct Curl_easy *data, + int ai_family, + unsigned int index); + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data, + int sockindex); +bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex); +#else +#define Curl_conn_dns_get_https(a, b) NULL +#define Curl_conn_dns_resolved_https(a, b) TRUE +#endif + +extern struct Curl_cftype Curl_cft_dns; + +#endif /* HEADER_CURL_CF_DNS_H */
diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c index 69e050b..3c2c837 100644 --- a/Utilities/cmcurl/lib/cf-h1-proxy.c +++ b/Utilities/cmcurl/lib/cf-h1-proxy.c
@@ -171,8 +171,8 @@ proxy */ /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request - after we have connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); + after we have connected. Let's thus free and clear it here. */ + curlx_safefree(data->req.proxyuserpwd); break; } } @@ -208,7 +208,7 @@ /* This only happens if we have looped here due to authentication reasons, and we do not really use the newly cloned URL here then. Free it. */ - Curl_safefree(data->req.newurl); + curlx_safefree(data->req.newurl); result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); if(result) @@ -449,7 +449,7 @@ if(!nread) { if(data->set.proxyauth && data->state.authproxy.avail && - data->state.aptr.proxyuserpwd) { + data->req.proxyuserpwd) { /* proxy auth was requested and there was proxy auth available, then deem this as "mere" proxy disconnect */ ts->close_connection = TRUE; @@ -465,17 +465,7 @@ if(ts->keepon == KEEPON_IGNORE) { /* This means we are currently ignoring a response-body */ - - if(ts->cl) { - /* A Content-Length based body: count down the counter - and make sure to break out of the loop when we are done! */ - ts->cl--; - if(ts->cl <= 0) { - ts->keepon = KEEPON_DONE; - break; - } - } - else if(ts->chunked_encoding) { + if(ts->chunked_encoding) { /* chunked-encoded body, so we need to do the chunked dance properly to know when the end of the body is reached */ size_t consumed = 0; @@ -491,6 +481,15 @@ ts->keepon = KEEPON_DONE; } } + else if(ts->cl) { + /* A Content-Length based body: count down the counter + and make sure to break out of the loop when we are done! */ + ts->cl--; + if(ts->cl <= 0) { + ts->keepon = KEEPON_DONE; + break; + } + } continue; } @@ -597,6 +596,8 @@ /* read what is there */ CURL_TRC_CF(data, cf, "CONNECT receive"); result = recv_CONNECT_resp(cf, data, ts, &done); + if(result) + CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result); if(!result) result = Curl_pgrsUpdate(data); /* error or not complete yet. return for more multi-multi */ @@ -642,10 +643,10 @@ DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); if(data->info.httpproxycode / 100 != 2) { /* a non-2xx response and we have no next URL to try. */ - Curl_safefree(data->req.newurl); + curlx_safefree(data->req.newurl); h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); - return CURLE_RECV_ERROR; + return CURLE_COULDNT_CONNECT; } /* 2xx response, SUCCESS! */ h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); @@ -689,7 +690,7 @@ result = H1_CONNECT(cf, data, ts); if(result) goto out; - Curl_safefree(data->state.aptr.proxyuserpwd); + curlx_safefree(data->req.proxyuserpwd); out: *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c index 4275e1e..2f8cc41 100644 --- a/Utilities/cmcurl/lib/cf-h2-proxy.c +++ b/Utilities/cmcurl/lib/cf-h2-proxy.c
@@ -80,7 +80,7 @@ struct tunnel_stream *ts) { const char *hostname; - int port; + uint16_t port; bool ipv6_ip; ts->state = H2_TUNNEL_INIT; @@ -92,7 +92,7 @@ Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); /* host:port with IPv6 support */ - ts->authority = curl_maprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname, + ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, ipv6_ip ? "]" : "", port); if(!ts->authority) return CURLE_OUT_OF_MEMORY; @@ -105,7 +105,7 @@ Curl_http_resp_free(ts->resp); Curl_bufq_free(&ts->recvbuf); Curl_bufq_free(&ts->sendbuf); - Curl_safefree(ts->authority); + curlx_safefree(ts->authority); memset(ts, 0, sizeof(*ts)); ts->state = H2_TUNNEL_INIT; } @@ -157,8 +157,8 @@ ts->state = new_state; /* If a proxy-authorization header was used for the proxy, then we should make sure that it is not accidentally used for the document request - after we have connected. So let's free and clear it here. */ - Curl_safefree(data->state.aptr.proxyuserpwd); + after we have connected. Let's thus free and clear it here. */ + curlx_safefree(data->req.proxyuserpwd); break; } } @@ -477,7 +477,7 @@ * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if(CURL_WANT_SEND(data)) { + if(CURL_REQ_WANT_SEND(data)) { drain_tunnel(cf, data, &ctx->tunnel); } break; @@ -512,7 +512,7 @@ } break; case NGHTTP2_WINDOW_UPDATE: - if(CURL_WANT_SEND(data)) { + if(CURL_REQ_WANT_SEND(data)) { drain_tunnel(cf, data, &ctx->tunnel); } break; @@ -627,7 +627,7 @@ if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf)) *data_flags = NGHTTP2_DATA_FLAG_EOF; - CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd", + CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zu", ts->stream_id, nread); return (nread > SSIZE_MAX) ? NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nread; @@ -675,7 +675,7 @@ if(stream_id != ctx->tunnel.stream_id) return 0; - CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)", + CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %u)", stream_id, nghttp2_http2_strerror(error_code), error_code); ctx->tunnel.closed = TRUE; ctx->tunnel.error = error_code; @@ -685,15 +685,16 @@ return 0; } -static CURLcode proxy_h2_submit(int32_t *pstream_id, - struct Curl_cfilter *cf, - struct Curl_easy *data, - nghttp2_session *h2, - struct httpreq *req, - const nghttp2_priority_spec *pri_spec, - void *stream_user_data, - nghttp2_data_source_read_callback read_callback, - void *read_ctx) +static CURLcode proxy_h2_submit( + int32_t *pstream_id, + struct Curl_cfilter *cf, + struct Curl_easy *data, + nghttp2_session *h2, + struct httpreq *req, + const nghttp2_priority_spec *pri_spec, + void *stream_user_data, + nghttp2_data_source_read_callback read_callback, + void *read_ctx) { struct dynhds h2_headers; nghttp2_nv *nva = NULL; @@ -804,14 +805,14 @@ return result; if(data->req.newurl) { /* Indicator that we should try again */ - Curl_safefree(data->req.newurl); + curlx_safefree(data->req.newurl); h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data); return CURLE_OK; } } /* Seems to have failed */ - return CURLE_RECV_ERROR; + return CURLE_COULDNT_CONNECT; } static CURLcode H2_CONNECT(struct Curl_cfilter *cf, @@ -1158,10 +1159,9 @@ *pnread = 0; if(ctx->tunnel.error) { - failf(data, "HTTP/2 stream %" PRIu32 " reset by %s (error 0x%" PRIx32 - " %s)", ctx->tunnel.stream_id, - ctx->tunnel.reset ? "server" : "curl", - ctx->tunnel.error, nghttp2_http2_strerror(ctx->tunnel.error)); + failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)", + ctx->tunnel.stream_id, ctx->tunnel.reset ? "server" : "curl", + ctx->tunnel.error, nghttp2_http2_strerror(ctx->tunnel.error)); return CURLE_RECV_ERROR; } @@ -1269,7 +1269,7 @@ } result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten); - CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zd", result, *pnwritten); + CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", result, *pnwritten); if(result && (result != CURLE_AGAIN)) goto out;
diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c index 782253e..9ee5e79 100644 --- a/Utilities/cmcurl/lib/cf-haproxy.c +++ b/Utilities/cmcurl/lib/cf-haproxy.c
@@ -28,6 +28,7 @@ #include "urldata.h" #include "cfilters.h" #include "cf-haproxy.h" +#include "curl_addrinfo.h" #include "curl_trc.h" #include "select.h" @@ -61,9 +62,16 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf, struct Curl_easy *data) { + /* We fake a client connection report to the upstream server + * with the HAProxy protocol, reporting the client's source + * and destination IP addresses and ports. + * addresses: either the ones used to talk to the upstream + * OR the value supplied by the user + * ports: the ports used in the upstream connection */ + const char *client_source_ip; + const char *client_dest_ip; struct cf_haproxy_ctx *ctx = cf->ctx; CURLcode result; - const char *client_ip; struct ip_quadruple ipquad; bool is_ipv6; @@ -79,15 +87,19 @@ if(result) return result; - /* Emit the correct prefix for IPv6 */ - if(data->set.str[STRING_HAPROXY_CLIENT_IP]) - client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; - else - client_ip = ipquad.local_ip; + if(data->set.str[STRING_HAPROXY_CLIENT_IP]) { + client_source_ip = data->set.str[STRING_HAPROXY_CLIENT_IP]; + client_dest_ip = client_source_ip; + is_ipv6 = !Curl_is_ipv4addr(client_source_ip); + } + else { + client_source_ip = ipquad.local_ip; + client_dest_ip = ipquad.remote_ip; + } result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n", is_ipv6 ? "TCP6" : "TCP4", - client_ip, ipquad.remote_ip, + client_source_ip, client_dest_ip, ipquad.local_port, ipquad.remote_port); #ifdef USE_UNIX_SOCKETS @@ -185,7 +197,7 @@ struct Curl_cftype Curl_cft_haproxy = { "HAPROXY", - CF_TYPE_PROXY, + CF_TYPE_PROXY | CF_TYPE_SETUP, 0, cf_haproxy_destroy, cf_haproxy_connect,
diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c index cabc6ab..d1d8f51 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.c +++ b/Utilities/cmcurl/lib/cf-https-connect.c
@@ -28,6 +28,7 @@ #include "urldata.h" #include "curl_trc.h" #include "cfilters.h" +#include "cf-dns.h" #include "connect.h" #include "hostip.h" #include "httpsrr.h" @@ -39,6 +40,7 @@ #include "vquic/vquic.h" typedef enum { + CF_HC_RESOLV, CF_HC_INIT, CF_HC_CONNECT, CF_HC_SUCCESS, @@ -56,19 +58,17 @@ BIT(shutdown); }; -static void cf_hc_baller_reset(struct cf_hc_baller *b, - struct Curl_easy *data) +static void cf_hc_baller_discard(struct cf_hc_baller *b, + struct Curl_easy *data) { if(b->cf) { Curl_conn_cf_close(b->cf, data); Curl_conn_cf_discard_chain(&b->cf, data); b->cf = NULL; } - b->result = CURLE_OK; - b->reply_ms = -1; } -static bool cf_hc_baller_is_active(struct cf_hc_baller *b) +static bool cf_hc_baller_is_connecting(struct cf_hc_baller *b) { return b->cf && !b->result; } @@ -112,18 +112,46 @@ cf_hc_state state; struct curltime started; /* when connect started */ CURLcode result; /* overall result */ + CURLcode check_h3_result; struct cf_hc_baller ballers[2]; size_t baller_count; timediff_t soft_eyeballs_timeout_ms; timediff_t hard_eyeballs_timeout_ms; + uint8_t def_transport; + BIT(httpsrr_resolved); + BIT(checked_h3); + BIT(ballers_complete); }; +static void cf_hc_ctx_close(struct Curl_easy *data, + struct cf_hc_ctx *ctx) +{ + if(ctx) { + size_t i; + for(i = 0; i < ctx->baller_count; ++i) + cf_hc_baller_discard(&ctx->ballers[i], data); + } +} + +static void cf_hc_ctx_destroy(struct Curl_easy *data, + struct cf_hc_ctx *ctx) +{ + if(ctx) { + cf_hc_ctx_close(data, ctx); + curlx_free(ctx); + } +} + static void cf_hc_baller_assign(struct cf_hc_baller *b, enum alpnid alpn_id, uint8_t def_transport) { b->alpn_id = alpn_id; b->transport = def_transport; + b->cf = NULL; + b->result = CURLE_OK; + b->reply_ms = -1; + b->shutdown = FALSE; switch(b->alpn_id) { case ALPN_h3: b->name = "h3"; @@ -135,6 +163,9 @@ case ALPN_h1: b->name = "h1"; break; + case ALPN_none: + b->name = "no-alpn"; + break; default: b->result = CURLE_FAILED_INIT; break; @@ -143,24 +174,14 @@ static void cf_hc_baller_init(struct cf_hc_baller *b, struct Curl_cfilter *cf, - struct Curl_easy *data, - uint8_t transport) + struct Curl_easy *data) { struct Curl_cfilter *save = cf->next; cf->next = NULL; b->started = *Curl_pgrs_now(data); - switch(b->alpn_id) { - case ALPN_h3: - transport = TRNSPRT_QUIC; - break; - default: - break; - } - - if(!b->result) - b->result = Curl_cf_setup_insert_after(cf, data, transport, - CURL_CF_SSL_ENABLE); + b->result = Curl_cf_setup_insert_after(cf, data, b->transport, + CURL_CF_SSL_ENABLE); b->cf = cf->next; cf->next = save; } @@ -179,59 +200,39 @@ return b->result; } -static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct cf_hc_ctx *ctx = cf->ctx; - size_t i; - - if(ctx) { - for(i = 0; i < ctx->baller_count; ++i) - cf_hc_baller_reset(&ctx->ballers[i], data); - ctx->state = CF_HC_INIT; - ctx->result = CURLE_OK; - ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; - ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4; - } -} - static CURLcode baller_connected(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_hc_baller *winner) { struct cf_hc_ctx *ctx = cf->ctx; - CURLcode result = CURLE_OK; - int reply_ms; - size_t i; - DEBUGASSERT(winner->cf); - for(i = 0; i < ctx->baller_count; ++i) - if(winner != &ctx->ballers[i]) - cf_hc_baller_reset(&ctx->ballers[i], data); + /* Make the winner's connection filter out own sub-filter, check, move, + * close all remaining. */ + if(cf->next) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + if(!winner->cf) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } - reply_ms = cf_hc_baller_reply_ms(winner, data); - if(reply_ms >= 0) - CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms", - winner->name, - (int)curlx_ptimediff_ms(Curl_pgrs_now(data), - &winner->started), reply_ms); - else - CURL_TRC_CF(data, cf, "deferred handshake %s: %dms", - winner->name, (int)curlx_ptimediff_ms(Curl_pgrs_now(data), - &winner->started)); - - /* install the winning filter below this one. */ cf->next = winner->cf; winner->cf = NULL; - /* whatever errors where reported by ballers, clear our errorbuf */ + ctx->state = CF_HC_SUCCESS; + cf->connected = TRUE; + + cf_hc_ctx_close(data, ctx); + /* ballers may have failf()'d, the winner resets it, so our + * errorbuf is clean again. */ Curl_reset_fail(data); #ifdef USE_NGHTTP2 { - /* Using nghttp2, we add the filter "below" us, so when the conn - * closes, we tear it down for a fresh reconnect */ + /* For a negotiated HTTP/2 connection insert the h2 filter. */ const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data); if(alpn && !strcmp("h2", alpn)) { - result = Curl_http2_switch_at(cf, data); + CURLcode result = Curl_http2_switch_at(cf, data); if(result) { ctx->state = CF_HC_FAILURE; ctx->result = result; @@ -240,63 +241,249 @@ } } #endif - - ctx->state = CF_HC_SUCCESS; - cf->connected = TRUE; - return result; + return CURLE_OK; } -static bool time_to_start_next(struct Curl_cfilter *cf, - struct Curl_easy *data, - size_t idx, struct curltime now) +static bool time_to_start_baller2(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_hc_ctx *ctx = cf->ctx; timediff_t elapsed_ms; - size_t i; - if(idx >= ctx->baller_count) + if(ctx->baller_count < 2) return FALSE; - if(cf_hc_baller_has_started(&ctx->ballers[idx])) + else if(cf_hc_baller_has_started(&ctx->ballers[1])) return FALSE; - for(i = 0; i < idx; i++) { - if(!ctx->ballers[i].result) - break; - } - if(i == idx) { - CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s", - ctx->ballers[idx].name); + else if(ctx->ballers[0].result) { + CURL_TRC_CF(data, cf, "%s baller failed, starting %s", + ctx->ballers[0].name, ctx->ballers[1].name); return TRUE; } - elapsed_ms = curlx_ptimediff_ms(&now, &ctx->started); + + elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - CURL_TRC_CF(data, cf, "hard timeout of %" FMT_TIMEDIFF_T "ms reached, " - "starting %s", - ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name); + CURL_TRC_CF(data, cf, "%s inconclusive after %" FMT_TIMEDIFF_T ", " + "starting %s", ctx->ballers[0].name, + ctx->hard_eyeballs_timeout_ms, ctx->ballers[1].name); return TRUE; } - - if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) { - if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) { - CURL_TRC_CF(data, cf, "soft timeout of %" FMT_TIMEDIFF_T "ms reached, " - "%s has not seen any data, starting %s", - ctx->soft_eyeballs_timeout_ms, - ctx->ballers[idx - 1].name, ctx->ballers[idx].name); + else if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) { + if(cf_hc_baller_reply_ms(&ctx->ballers[0], data) < 0) { + CURL_TRC_CF(data, cf, "%s has not seen any data after %" + FMT_TIMEDIFF_T "ms, starting %s", + ctx->ballers[0].name, ctx->soft_eyeballs_timeout_ms, + ctx->ballers[1].name); return TRUE; } - /* set the effective hard timeout again */ - Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms, - EXPIRE_ALPN_EYEBALLS); } return FALSE; } +static bool cf_hc_may_h3(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + if(!ctx->checked_h3) { + ctx->check_h3_result = + Curl_conn_may_http3(data, cf->conn, ctx->def_transport); + ctx->checked_h3 = TRUE; + } + return !ctx->check_h3_result; +} + +static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + enum alpnid not_this_one) +{ +#ifdef USE_HTTPSRR + /* Is there an HTTPSRR use its ALPNs here. + * We are here after having selected a connection to a host+port and + * can no longer change that. Any HTTPSRR advice for other hosts and ports + * we need to ignore. */ + const struct Curl_https_rrinfo *rr; + size_t i; + + /* Do we have HTTPS-RR information? */ + rr = Curl_conn_dns_get_https(data, cf->sockindex); + + /* We do not support `rr->no_def_alpn`. */ + if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) { + for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) { + enum alpnid alpn_rr = (enum alpnid)rr->alpns[i]; + if(alpn_rr == not_this_one) /* don't want this one */ + continue; + switch(alpn_rr) { + case ALPN_h3: + if((data->state.http_neg.allowed & CURL_HTTP_V3x) && + cf_hc_may_h3(cf, data)) { + return alpn_rr; + } + break; + case ALPN_h2: + if(data->state.http_neg.allowed & CURL_HTTP_V2x) { + return alpn_rr; + } + break; + case ALPN_h1: + if(data->state.http_neg.allowed & CURL_HTTP_V1x) { + return alpn_rr; + } + break; + default: /* ignore */ + break; + } + } + } +#else + (void)cf; + (void)data; + (void)not_this_one; +#endif + return ALPN_none; +} + +static enum alpnid cf_hc_get_pref_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + enum alpnid not_this_one) +{ + if((data->state.http_neg.preferred & data->state.http_neg.allowed)) { + switch(data->state.http_neg.preferred) { + case CURL_HTTP_V3x: + if(cf_hc_may_h3(cf, data) && (ALPN_h3 != not_this_one)) + return ALPN_h3; + break; + case CURL_HTTP_V2x: + if(ALPN_h2 != not_this_one) + return ALPN_h2; + break; + case CURL_HTTP_V1x: + /* If we are trying h2 already, h1 is already used as fallback */ + if((ALPN_h1 != not_this_one) && (ALPN_h2 != not_this_one)) + return ALPN_h1; + break; + default: + break; + } + } + return ALPN_none; +} + +static enum alpnid cf_hc_get_first_alpn(struct Curl_cfilter *cf, + struct Curl_easy *data, + http_majors choices, + enum alpnid not_this_one) +{ + /* When told to not try h2, we also do not try h1 and vice versa */ + bool allow_h1_or_h2 = (not_this_one != ALPN_h1) && + (not_this_one != ALPN_h2); + if((ALPN_h3 != not_this_one) && (choices & CURL_HTTP_V3x) && + cf_hc_may_h3(cf, data)) { + return ALPN_h3; + } + if(allow_h1_or_h2 && (choices & CURL_HTTP_V2x)) { + return ALPN_h2; + } + if(allow_h1_or_h2 && (choices & CURL_HTTP_V1x)) { + return ALPN_h1; + } + return ALPN_none; +} + +static CURLcode cf_hc_set_baller1(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + enum alpnid alpn1 = ALPN_none; + VERBOSE(const char *source = "HTTPS-RR"); + + DEBUGASSERT(cf->conn->bits.tls_enable_alpn); + + alpn1 = cf_hc_get_httpsrr_alpn(cf, data, ALPN_none); + if(alpn1 == ALPN_none) { + /* preference is configured and allowed, can we use it? */ + VERBOSE(source = "preferred version"); + alpn1 = cf_hc_get_pref_alpn(cf, data, ALPN_none); + } + if(alpn1 == ALPN_none) { + VERBOSE(source = "wanted versions"); + alpn1 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.wanted, + ALPN_none); + } + if(alpn1 == ALPN_none) { + VERBOSE(source = "allowed versions"); + alpn1 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.allowed, + ALPN_none); + } + + if(alpn1 == ALPN_none) { + /* None of the wanted/allowed HTTP versions could be chosen */ + if(ctx->check_h3_result) { + CURL_TRC_CF(data, cf, "unable to use HTTP/3"); + return ctx->check_h3_result; + } + CURL_TRC_CF(data, cf, "unable to select HTTP version"); + return CURLE_FAILED_INIT; + } + + cf_hc_baller_assign(&ctx->ballers[0], alpn1, ctx->def_transport); + ctx->baller_count = 1; + CURL_TRC_CF(data, cf, "1st attempt uses %s from %s", + ctx->ballers[0].name, source); + + switch(alpn1) { + case ALPN_h1: + /* We really want h1, switch off h2 to make it disappear in ALPN */ + data->state.http_neg.wanted &= (uint8_t)~CURL_HTTP_V2x; + break; + default: + break; + } + + return CURLE_OK; +} + +static void cf_hc_set_baller2(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_hc_ctx *ctx = cf->ctx; + enum alpnid alpn2 = ALPN_none, alpn1 = ctx->ballers[0].alpn_id; + VERBOSE(const char *source = "HTTPS-RR"); + + if(ctx->ballers_complete) + return; /* already done */ + if(!ctx->httpsrr_resolved) + return; /* HTTPS-RR pending */ + + alpn2 = cf_hc_get_httpsrr_alpn(cf, data, alpn1); + if(alpn2 == ALPN_none) { + /* preference is configured and allowed, can we use it? */ + VERBOSE(source = "preferred version"); + alpn2 = cf_hc_get_pref_alpn(cf, data, alpn1); + } + if(alpn2 == ALPN_none) { + VERBOSE(source = "wanted versions"); + alpn2 = cf_hc_get_first_alpn(cf, data, + data->state.http_neg.wanted, + alpn1); + } + + if(alpn2 != ALPN_none) { + cf_hc_baller_assign(&ctx->ballers[1], alpn2, ctx->def_transport); + ctx->baller_count = 2; + CURL_TRC_CF(data, cf, "2nd attempt uses %s from %s", + ctx->ballers[1].name, source); + } + ctx->ballers_complete = TRUE; +} + static CURLcode cf_hc_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { struct cf_hc_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - size_t i, failed_ballers; if(cf->connected) { *done = TRUE; @@ -304,24 +491,45 @@ } *done = FALSE; + + if(!ctx->httpsrr_resolved) { + ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex); +#ifdef DEBUGBUILD + if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) { + CURL_TRC_CF(data, cf, "awaiting HTTPS-RR"); + return CURLE_OK; + } +#endif + } + switch(ctx->state) { + case CF_HC_RESOLV: + ctx->state = CF_HC_INIT; + FALLTHROUGH(); + case CF_HC_INIT: DEBUGASSERT(!cf->next); - for(i = 0; i < ctx->baller_count; i++) - DEBUGASSERT(!ctx->ballers[i].cf); CURL_TRC_CF(data, cf, "connect, init"); + result = cf_hc_set_baller1(cf, data); + if(result) { + ctx->result = result; + ctx->state = CF_HC_FAILURE; + goto out; + } + cf_hc_set_baller2(cf, data); ctx->started = *Curl_pgrs_now(data); - cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport); - if(ctx->baller_count > 1) { + cf_hc_baller_init(&ctx->ballers[0], cf, data); + if((ctx->baller_count > 1) || !ctx->ballers_complete) { Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); - CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T - "ms", ctx->soft_eyeballs_timeout_ms); } ctx->state = CF_HC_CONNECT; FALLTHROUGH(); case CF_HC_CONNECT: - if(cf_hc_baller_is_active(&ctx->ballers[0])) { + if(!ctx->ballers_complete) + cf_hc_set_baller2(cf, data); + + if(cf_hc_baller_is_connecting(&ctx->ballers[0])) { result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->ballers[0]); @@ -329,12 +537,11 @@ } } - if(time_to_start_next(cf, data, 1, *Curl_pgrs_now(data))) { - cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport); + if(time_to_start_baller2(cf, data)) { + cf_hc_baller_init(&ctx->ballers[1], cf, data); } - if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) { - CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name); + if(cf_hc_baller_is_connecting(&ctx->ballers[1])) { result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done); if(!result && *done) { result = baller_connected(cf, data, &ctx->ballers[1]); @@ -342,21 +549,12 @@ } } - failed_ballers = 0; - for(i = 0; i < ctx->baller_count; i++) { - if(ctx->ballers[i].result) - ++failed_ballers; - } - - if(failed_ballers == ctx->baller_count) { + if(ctx->ballers[0].result && + (ctx->ballers[1].result || + (ctx->ballers_complete && (ctx->baller_count < 2)))) { /* all have failed. we give up */ CURL_TRC_CF(data, cf, "connect, all attempts failed"); - for(i = 0; i < ctx->baller_count; i++) { - if(ctx->ballers[i].result) { - result = ctx->ballers[i].result; - break; - } - } + ctx->result = result = ctx->ballers[0].result; ctx->state = CF_HC_FAILURE; goto out; } @@ -400,7 +598,7 @@ for(i = 0; i < ctx->baller_count; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; bool bdone = FALSE; - if(!cf_hc_baller_is_active(b) || b->shutdown) + if(!cf_hc_baller_is_connecting(b) || b->shutdown) continue; b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone); if(b->result || bdone) @@ -433,11 +631,11 @@ for(i = 0; (i < ctx->baller_count) && !result; i++) { struct cf_hc_baller *b = &ctx->ballers[i]; - if(!cf_hc_baller_is_active(b)) + if(!cf_hc_baller_is_connecting(b)) continue; result = Curl_conn_cf_adjust_pollset(b->cf, data, ps); } - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); } return result; } @@ -536,7 +734,7 @@ static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data) { CURL_TRC_CF(data, cf, "close"); - cf_hc_reset(cf, data); + cf_hc_ctx_close(data, cf->ctx); cf->connected = FALSE; if(cf->next) { @@ -550,13 +748,12 @@ struct cf_hc_ctx *ctx = cf->ctx; CURL_TRC_CF(data, cf, "destroy"); - cf_hc_reset(cf, data); - Curl_safefree(ctx); + cf_hc_ctx_destroy(data, ctx); } struct Curl_cftype Curl_cft_http_connect = { "HTTPS-CONNECT", - 0, + CF_TYPE_SETUP | CF_TYPE_HTTPSRR, CURL_LOG_LVL_NONE, cf_hc_destroy, cf_hc_connect, @@ -574,59 +771,42 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, struct Curl_easy *data, - enum alpnid *alpnids, size_t alpn_count, uint8_t def_transport) { struct Curl_cfilter *cf = NULL; struct cf_hc_ctx *ctx; CURLcode result = CURLE_OK; - size_t i; ctx = curlx_calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } - - DEBUGASSERT(alpnids); - DEBUGASSERT(alpn_count); - DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers)); - if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) { - failf(data, "https-connect filter create with unsupported %zu ALPN ids", - alpn_count); - result = CURLE_FAILED_INIT; - goto out; - } - - for(i = 0; i < alpn_count; ++i) - cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport); - for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i) - ctx->ballers[i].alpn_id = ALPN_none; - ctx->baller_count = alpn_count; + ctx->def_transport = def_transport; + ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout; + ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2; result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx); if(result) goto out; ctx = NULL; - cf_hc_reset(cf, data); out: *pcf = result ? NULL : cf; - curlx_free(ctx); + cf_hc_ctx_destroy(data, ctx); return result; } -static CURLcode cf_http_connect_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - enum alpnid *alpn_ids, size_t alpn_count, - uint8_t def_transport) +static CURLcode cf_hc_add(struct Curl_easy *data, + struct connectdata *conn, + int sockindex, + uint8_t def_transport) { struct Curl_cfilter *cf; CURLcode result = CURLE_OK; DEBUGASSERT(data); - result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport); + result = cf_hc_create(&cf, data, def_transport); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -634,138 +814,19 @@ return result; } -static bool cf_https_alpns_contain(enum alpnid id, - enum alpnid *list, size_t len) -{ - size_t i; - for(i = 0; i < len; ++i) { - if(id == list[i]) - return TRUE; - } - return FALSE; -} - CURLcode Curl_cf_https_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex) { - enum alpnid alpn_ids[2]; - size_t alpn_count = 0; CURLcode result = CURLE_OK; - struct Curl_cfilter cf_fake, *cf = NULL; - (void)sockindex; - /* we want to log for the filter before we create it, fake it. */ - memset(&cf_fake, 0, sizeof(cf_fake)); - cf_fake.cft = &Curl_cft_http_connect; - cf = &cf_fake; + DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS); - if(conn->bits.tls_enable_alpn) { -#ifdef USE_HTTPSRR - /* Is there an HTTPSRR use its ALPNs here. - * We are here after having selected a connection to a host+port and - * can no longer change that. Any HTTPSRR advice for other hosts and ports - * we need to ignore. */ - struct Curl_dns_entry *dns = data->state.dns[sockindex]; - struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL; - if(rr && !rr->no_def_alpn && /* ALPNs are defaults */ - (!rr->target || /* for same host */ - !rr->target[0] || - (rr->target[0] == '.' && - !rr->target[1])) && - (rr->port < 0 || /* for same port */ - rr->port == conn->remote_port)) { - size_t i; - for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) && - alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) { - enum alpnid alpn = rr->alpns[i]; - if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count)) - continue; - switch(alpn) { - case ALPN_h3: - if(Curl_conn_may_http3(data, conn, conn->transport_wanted)) - break; /* not possible */ - if(data->state.http_neg.allowed & CURL_HTTP_V3x) { - CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - case ALPN_h2: - if(data->state.http_neg.allowed & CURL_HTTP_V2x) { - CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - case ALPN_h1: - if(data->state.http_neg.allowed & CURL_HTTP_V1x) { - CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR"); - alpn_ids[alpn_count++] = alpn; - } - break; - default: /* ignore */ - break; - } - } - } -#endif + if((conn->scheme->protocol != CURLPROTO_HTTPS) || + !conn->bits.tls_enable_alpn) + goto out; - /* Add preferred HTTP version ALPN first */ - if(data->state.http_neg.preferred && - (alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.preferred & data->state.http_neg.allowed)) { - enum alpnid alpn_pref = ALPN_none; - switch(data->state.http_neg.preferred) { - case CURL_HTTP_V3x: - if(!Curl_conn_may_http3(data, conn, conn->transport_wanted)) - alpn_pref = ALPN_h3; - break; - case CURL_HTTP_V2x: - alpn_pref = ALPN_h2; - break; - case CURL_HTTP_V1x: - alpn_pref = ALPN_h1; - break; - default: - break; - } - if(alpn_pref && - !cf_https_alpns_contain(alpn_pref, alpn_ids, alpn_count)) { - alpn_ids[alpn_count++] = alpn_pref; - } - } - - if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V3x) && - !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) { - result = Curl_conn_may_http3(data, conn, conn->transport_wanted); - if(!result) { - CURL_TRC_CF(data, cf, "adding wanted h3"); - alpn_ids[alpn_count++] = ALPN_h3; - } - else if(data->state.http_neg.wanted == CURL_HTTP_V3x) - goto out; /* only h3 allowed, not possible, error out */ - } - if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V2x) && - !cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) { - CURL_TRC_CF(data, cf, "adding wanted h2"); - alpn_ids[alpn_count++] = ALPN_h2; - } - else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && - (data->state.http_neg.wanted & CURL_HTTP_V1x) && - !cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) { - CURL_TRC_CF(data, cf, "adding wanted h1"); - alpn_ids[alpn_count++] = ALPN_h1; - } - } - - /* If we identified ALPNs to use, install our filter. Otherwise, - * install nothing, so our call will use a default connect setup. */ - if(alpn_count) { - result = cf_http_connect_add(data, conn, sockindex, - alpn_ids, alpn_count, - conn->transport_wanted); - } + result = cf_hc_add(data, conn, sockindex, conn->transport_wanted); out: return result;
diff --git a/Utilities/cmcurl/lib/cf-https-connect.h b/Utilities/cmcurl/lib/cf-https-connect.h index df29c8d..3160c03 100644 --- a/Utilities/cmcurl/lib/cf-https-connect.h +++ b/Utilities/cmcurl/lib/cf-https-connect.h
@@ -31,15 +31,9 @@ struct Curl_easy; struct connectdata; struct Curl_cftype; -struct Curl_dns_entry; extern struct Curl_cftype Curl_cft_http_connect; -CURLcode Curl_cf_http_connect_add(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool try_h3, bool try_h21); - CURLcode Curl_cf_https_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex);
diff --git a/Utilities/cmcurl/lib/cf-ip-happy.c b/Utilities/cmcurl/lib/cf-ip-happy.c index dfbf4ae..f67273e 100644 --- a/Utilities/cmcurl/lib/cf-ip-happy.c +++ b/Utilities/cmcurl/lib/cf-ip-happy.c
@@ -49,6 +49,7 @@ #include "urldata.h" #include "connect.h" #include "cfilters.h" +#include "cf-dns.h" #include "cf-ip-happy.h" #include "curl_addrinfo.h" #include "curl_trc.h" @@ -91,9 +92,11 @@ } #ifdef UNITTESTS -/* used by unit2600.c */ -void Curl_debug_set_transport_provider(uint8_t transport, - cf_ip_connect_create *cf_create) +/* @unittest 2600 */ +UNITTEST void debug_set_transport_provider( + uint8_t transport, cf_ip_connect_create *cf_create); +UNITTEST void debug_set_transport_provider( + uint8_t transport, cf_ip_connect_create *cf_create) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) { @@ -106,59 +109,46 @@ #endif /* UNITTESTS */ struct cf_ai_iter { - const struct Curl_addrinfo *head; - const struct Curl_addrinfo *last; + struct Curl_cfilter *cf; int ai_family; - int n; + unsigned int n; }; static void cf_ai_iter_init(struct cf_ai_iter *iter, - const struct Curl_addrinfo *list, + struct Curl_cfilter *cf, int ai_family) { - iter->head = list; + iter->cf = cf; iter->ai_family = ai_family; - iter->last = NULL; - iter->n = -1; + iter->n = 0; } -static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter) +static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter, + struct Curl_easy *data) { const struct Curl_addrinfo *addr; - if(iter->n < 0) { + + if(!iter->cf) + return NULL; + + addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex, + iter->ai_family, iter->n); + if(addr) iter->n++; - for(addr = iter->head; addr; addr = addr->ai_next) { - if(addr->ai_family == iter->ai_family) - break; - } - iter->last = addr; - } - else if(iter->last) { - iter->n++; - for(addr = iter->last->ai_next; addr; addr = addr->ai_next) { - if(addr->ai_family == iter->ai_family) - break; - } - iter->last = addr; - } - return iter->last; + return addr; } -static bool cf_ai_iter_has_more(struct cf_ai_iter *iter) +static bool cf_ai_iter_has_more(struct cf_ai_iter *iter, + struct Curl_easy *data) { - const struct Curl_addrinfo *addr = iter->last ? iter->last->ai_next : - ((iter->n < 0) ? iter->head : NULL); - while(addr) { - if(addr->ai_family == iter->ai_family) - return TRUE; - addr = addr->ai_next; - } - return FALSE; + return (iter->cf && + !!Curl_conn_dns_get_ai(data, iter->cf->sockindex, + iter->ai_family, iter->n)); } struct cf_ip_attempt { struct cf_ip_attempt *next; - const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */ + struct Curl_sockaddr_ex addr; struct Curl_cfilter *cf; /* current sub-cfilter connecting */ cf_ip_connect_create *cf_create; struct curltime started; /* start of current attempt */ @@ -185,7 +175,7 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa, struct Curl_cfilter *cf, struct Curl_easy *data, - const struct Curl_addrinfo *addr, + struct Curl_sockaddr_ex *addr, int ai_family, uint8_t transport, cf_ip_connect_create *cf_create) @@ -199,14 +189,14 @@ if(!a) return CURLE_OUT_OF_MEMORY; - a->addr = addr; + a->addr = *addr; a->ai_family = ai_family; a->transport = transport; a->result = CURLE_OK; a->cf_create = cf_create; *pa = a; - result = a->cf_create(&a->cf, data, cf->conn, a->addr, transport); + result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport); if(result) goto out; @@ -238,8 +228,12 @@ a->connected = TRUE; } } - else if(a->result == CURLE_WEIRD_SERVER_REPLY) - a->inconclusive = TRUE; + else { + if(a->result == CURLE_WEIRD_SERVER_REPLY) + a->inconclusive = TRUE; + if(a->cf) + Curl_conn_cf_discard_chain(&a->cf, data); + } } return a->result; } @@ -256,6 +250,7 @@ struct curltime last_attempt_started; timediff_t attempt_delay_ms; int last_attempt_ai_family; + uint32_t max_concurrent; uint8_t transport; }; @@ -263,19 +258,18 @@ struct Curl_cfilter *cf, struct Curl_easy *data) { - struct Curl_cfilter *cf_prev = a->cf; struct Curl_cfilter *wcf; CURLcode result; - /* When restarting, we tear down and existing filter *after* we - * started up the new one. This gives us a new socket number and - * probably a new local port. Which may prevent confusion. */ + if(a->cf) + Curl_conn_cf_discard_chain(&a->cf, data); + a->result = CURLE_OK; a->connected = FALSE; a->inconclusive = FALSE; a->cf = NULL; - result = a->cf_create(&a->cf, data, cf->conn, a->addr, a->transport); + result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport); if(!result) { bool dummy; /* the new filter might have sub-filters */ @@ -285,8 +279,6 @@ } a->result = cf_ip_attempt_connect(a, data, &dummy); } - if(cf_prev) - Curl_conn_cf_discard_chain(&cf_prev, data); return result; } @@ -304,54 +296,76 @@ bs->winner = NULL; } -static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version, - const struct Curl_addrinfo *addr_list, +static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, + struct Curl_cfilter *cf, cf_ip_connect_create *cf_create, uint8_t transport, - timediff_t attempt_delay_ms) + timediff_t attempt_delay_ms, + uint32_t max_concurrent) { memset(bs, 0, sizeof(*bs)); bs->cf_create = cf_create; bs->transport = transport; bs->attempt_delay_ms = attempt_delay_ms; + bs->max_concurrent = max_concurrent; bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */ if(transport == TRNSPRT_UNIX) { #ifdef USE_UNIX_SOCKETS - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_UNIX); + cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX); #else return CURLE_UNSUPPORTED_PROTOCOL; #endif } else { /* TCP/UDP/QUIC */ #ifdef USE_IPV6 - if(ip_version == CURL_IPRESOLVE_V6) - cf_ai_iter_init(&bs->addr_iter, NULL, AF_INET); - else - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET); - - if(ip_version == CURL_IPRESOLVE_V4) - cf_ai_iter_init(&bs->ipv6_iter, NULL, AF_INET6); - else - cf_ai_iter_init(&bs->ipv6_iter, addr_list, AF_INET6); -#else - (void)ip_version; - cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET); + cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6); #endif + cf_ai_iter_init(&bs->addr_iter, cf, AF_INET); } return CURLE_OK; } +static void cf_ip_ballers_prune(struct cf_ip_ballers *bs, + struct Curl_cfilter *cf, + struct Curl_easy *data, + uint32_t max_concurrent) +{ + struct cf_ip_attempt *a = NULL, **panchor; + uint32_t ongoing = 0; + + for(a = bs->running; a; a = a->next) { + if(!a->result && !a->connected) + ++ongoing; + } + + panchor = &bs->running; + while(*panchor && (ongoing > max_concurrent)) { + a = *panchor; + if(!a->result && !a->connected) { + *panchor = a->next; + a->next = NULL; + cf_ip_attempt_free(a, data); + --ongoing; + CURL_TRC_CF(data, cf, "discarding oldest attempt to keep limit"); + } + else { + panchor = &a->next; + } + } +} + static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs, struct Curl_cfilter *cf, struct Curl_easy *data, + bool dns_resolved, bool *connected) { CURLcode result = CURLE_OK; struct cf_ip_attempt *a = NULL, **panchor; bool do_more; timediff_t next_expire_ms; - int inconclusive, ongoing; + uint32_t inconclusive, ongoing; VERBOSE(int i); if(bs->winner) @@ -389,7 +403,7 @@ } if(bs->running) CURL_TRC_CF(data, cf, "checked connect attempts: " - "%d ongoing, %d inconclusive", ongoing, inconclusive); + "%u ongoing, %u inconclusive", ongoing, inconclusive); /* no attempt connected yet, start another one? */ if(!ongoing) { @@ -398,10 +412,10 @@ do_more = TRUE; } else { - bool more_possible = cf_ai_iter_has_more(&bs->addr_iter); + bool more_possible = cf_ai_iter_has_more(&bs->addr_iter, data); #ifdef USE_IPV6 if(!more_possible) - more_possible = cf_ai_iter_has_more(&bs->ipv6_iter); + more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data); #endif do_more = more_possible && (curlx_ptimediff_ms(Curl_pgrs_now(data), &bs->last_attempt_started) >= @@ -414,27 +428,42 @@ if(do_more) { /* start the next attempt if there is another ip address to try. * Alternate between address families when possible. */ - const struct Curl_addrinfo *addr = NULL; + const struct Curl_addrinfo *ai = NULL; int ai_family = 0; + CURL_TRC_CF(data, cf, "want to do more"); #ifdef USE_IPV6 if((bs->last_attempt_ai_family == AF_INET) || - !cf_ai_iter_has_more(&bs->addr_iter)) { - addr = cf_ai_iter_next(&bs->ipv6_iter); + !cf_ai_iter_has_more(&bs->addr_iter, data)) { + ai = cf_ai_iter_next(&bs->ipv6_iter, data); ai_family = bs->ipv6_iter.ai_family; + CURL_TRC_CF(data, cf, "check for next AAAA address: %s", + ai ? "found" : "none"); } #endif - if(!addr) { - addr = cf_ai_iter_next(&bs->addr_iter); + if(!ai) { + ai = cf_ai_iter_next(&bs->addr_iter, data); ai_family = bs->addr_iter.ai_family; + CURL_TRC_CF(data, cf, "check for next A address: %s", + ai ? "found" : "none"); } /* We are (re-)starting attempts. We are not interested in * keeping old failure information. The new attempt will either * succeed or persist new failure. */ Curl_reset_fail(data); - if(addr) { /* try another address */ - result = cf_ip_attempt_new(&a, cf, data, addr, ai_family, - bs->transport, bs->cf_create); + if(ai) { /* try another address */ + struct Curl_sockaddr_ex addr; + + /* Discard oldest to make room for new attempt */ + if(bs->max_concurrent) + cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1); + + result = Curl_socket_addr_from_ai(&addr, ai, bs->transport); + if(result) + goto out; + + result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family, + bs->transport, bs->cf_create); CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d", bs->running ? "next" : "first", (ai_family == AF_INET) ? "4" : "6", result); @@ -483,7 +512,7 @@ /* attempt timeout for restart has not expired yet */ goto out; } - else if(!ongoing) { + else if(!ongoing && dns_resolved) { /* no more addresses, no inconclusive attempts */ CURL_TRC_CF(data, cf, "no more attempts to try"); result = CURLE_COULDNT_CONNECT; @@ -509,10 +538,10 @@ return CURLE_OPERATION_TIMEDOUT; } - more_possible = cf_ai_iter_has_more(&bs->addr_iter); + more_possible = cf_ai_iter_has_more(&bs->addr_iter, data); #ifdef USE_IPV6 if(!more_possible) - more_possible = cf_ai_iter_has_more(&bs->ipv6_iter); + more_possible = cf_ai_iter_has_more(&bs->ipv6_iter, data); #endif if(more_possible) { timediff_t expire_ms, elapsed_ms; @@ -543,7 +572,7 @@ *done = TRUE; for(a = bs->running; a; a = a->next) { bool bdone = FALSE; - if(a->shutdown) + if(a->shutdown || !a->cf) continue; a->result = a->cf->cft->do_shutdown(a->cf, data, &bdone); if(a->result || bdone) @@ -576,7 +605,7 @@ for(a = bs->running; a; a = a->next) { if(a->result) continue; - if(a->cf->cft->has_data_pending(a->cf, data)) + if(a->cf && a->cf->cft->has_data_pending(a->cf, data)) return TRUE; } return FALSE; @@ -592,7 +621,7 @@ memset(&tmax, 0, sizeof(tmax)); for(a = bs->running; a; a = a->next) { memset(&t, 0, sizeof(t)); - if(!a->cf->cft->query(a->cf, data, query, NULL, &t)) { + if(a->cf && !a->cf->cft->query(a->cf, data, query, NULL, &t)) { if((t.tv_sec || t.tv_usec) && curlx_ptimediff_us(&t, &tmax) > 0) tmax = t; } @@ -607,8 +636,8 @@ struct cf_ip_attempt *a; for(a = bs->running; a; a = a->next) { - if(!a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS, - &breply_ms, NULL)) { + if(a->cf && !a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS, + &breply_ms, NULL)) { if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms)) reply_ms = breply_ms; } @@ -628,6 +657,7 @@ cf_connect_state state; struct cf_ip_ballers ballers; struct curltime started; + BIT(dns_resolved); }; static CURLcode is_connected(struct Curl_cfilter *cf, @@ -638,12 +668,12 @@ struct connectdata *conn = cf->conn; CURLcode result; - result = cf_ip_ballers_run(&ctx->ballers, cf, data, connected); + result = cf_ip_ballers_run(&ctx->ballers, cf, data, + (bool)ctx->dns_resolved, connected); if(!result) return CURLE_OK; - - { + else { const char *hostname, *proxy_name = NULL; char viamsg[160]; #ifndef CURL_DISABLE_PROXY @@ -662,14 +692,14 @@ else #endif { - int port; + uint16_t port; if(cf->sockindex == SECONDARYSOCKET) port = conn->secondary_port; else if(cf->conn->bits.conn_to_port) port = conn->conn_to_port; else port = conn->remote_port; - curl_msnprintf(viamsg, sizeof(viamsg), "port %u", port); + curl_msnprintf(viamsg, sizeof(viamsg), "port %d", port); } failf(data, "Failed to connect to %s %s %s%s%safter " @@ -681,28 +711,22 @@ curlx_ptimediff_ms(Curl_pgrs_now(data), &data->progress.t_startsingle), curl_easy_strerror(result)); - } #ifdef SOCKETIMEDOUT - if(SOCKETIMEDOUT == data->state.os_errno) - result = CURLE_OPERATION_TIMEDOUT; + if(SOCKETIMEDOUT == data->state.os_errno) + result = CURLE_OPERATION_TIMEDOUT; #endif - return result; + return result; + } } -/* - * Connect to the given host with timeout, proxy or remote does not matter. - * There might be more than one IP address to try out. - */ -static CURLcode start_connect(struct Curl_cfilter *cf, - struct Curl_easy *data) +#define IP_HE_MAX_CONCURRENT_ATTEMPTS 6 + +static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_ip_happy_ctx *ctx = cf->ctx; - struct Curl_dns_entry *dns = data->state.dns[cf->sockindex]; - - if(!dns) - return CURLE_FAILED_INIT; if(Curl_timeleft_ms(data) < 0) { /* a precaution, no need to continue if time already is up */ @@ -712,9 +736,10 @@ CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport); ctx->started = *Curl_pgrs_now(data); - return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version, - dns->addr, ctx->cf_create, ctx->transport, - data->set.happy_eyeballs_timeout); + return cf_ip_ballers_init(&ctx->ballers, cf, + ctx->cf_create, ctx->transport, + data->set.happy_eyeballs_timeout, + IP_HE_MAX_CONCURRENT_ATTEMPTS); } static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf, @@ -727,6 +752,12 @@ cf_ip_ballers_clear(cf, data, &ctx->ballers); } +static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx) +{ + if(ctx) + curlx_free(ctx); +} + static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -754,7 +785,7 @@ if(!cf->connected) { result = cf_ip_ballers_pollset(&ctx->ballers, data, ps); - CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n); + CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n); } return result; } @@ -766,6 +797,10 @@ struct cf_ip_happy_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; + /* -Werror=null-dereference finds false positives suddenly. */ + if(!data) + return CURLE_FAILED_INIT; + if(cf->connected) { *done = TRUE; return CURLE_OK; @@ -774,13 +809,24 @@ DEBUGASSERT(ctx); *done = FALSE; + if(!ctx->dns_resolved) { + result = Curl_conn_dns_result(cf->conn, cf->sockindex); + if(!result) + ctx->dns_resolved = TRUE; + else if(result == CURLE_AGAIN) { + result = CURLE_OK; + } + else /* real error */ + goto out; + } + switch(ctx->state) { case SCFST_INIT: DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data)); DEBUGASSERT(!cf->connected); - result = start_connect(cf, data); + result = cf_ip_happy_init(cf, data); if(result) - return result; + goto out; ctx->state = SCFST_WAITING; FALLTHROUGH(); case SCFST_WAITING: @@ -808,8 +854,7 @@ bool is_ipv6; if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) { const char *host; - int port; - Curl_conn_get_current_host(data, cf->sockindex, &host, &port); + Curl_conn_get_current_host(data, cf->sockindex, &host, NULL); CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u", host, ipquad.remote_ip, ipquad.remote_port); } @@ -822,6 +867,7 @@ *done = TRUE; break; } +out: return result; } @@ -895,14 +941,13 @@ CURL_TRC_CF(data, cf, "destroy"); if(ctx) { cf_ip_happy_ctx_clear(cf, data); + cf_ip_happy_ctx_destroy(ctx); } - /* release any resources held in state */ - Curl_safefree(ctx); } struct Curl_cftype Curl_cft_ip_happy = { "HAPPY-EYEBALLS", - 0, + CF_TYPE_SETUP, CURL_LOG_LVL_NONE, cf_ip_happy_destroy, cf_ip_happy_connect, @@ -952,8 +997,8 @@ out: if(result) { - Curl_safefree(*pcf); - curlx_free(ctx); + curlx_safefree(*pcf); + cf_ip_happy_ctx_destroy(ctx); } return result; }
diff --git a/Utilities/cmcurl/lib/cf-ip-happy.h b/Utilities/cmcurl/lib/cf-ip-happy.h index 71f2122..547ee4b 100644 --- a/Utilities/cmcurl/lib/cf-ip-happy.h +++ b/Utilities/cmcurl/lib/cf-ip-happy.h
@@ -25,6 +25,12 @@ ***************************************************************************/ #include "curl_setup.h" +struct connectdata; +struct Curl_addrinfo; +struct Curl_cfilter; +struct Curl_easy; +struct Curl_sockaddr_ex; + /** * Create a cfilter for making an "ip" connection to the * given address, using parameters from `conn`. The "ip" connection @@ -39,7 +45,7 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at, @@ -48,9 +54,4 @@ extern struct Curl_cftype Curl_cft_ip_happy; -#ifdef UNITTESTS -void Curl_debug_set_transport_provider(uint8_t transport, - cf_ip_connect_create *cf_create); -#endif - #endif /* HEADER_CURL_IP_HAPPY_H */
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c index 2ebbe4d..b99bcde 100644 --- a/Utilities/cmcurl/lib/cf-socket.c +++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -71,6 +71,7 @@ #include "curlx/strdup.h" #include "system_win32.h" #include "curlx/nonblock.h" +#include "curlx/strcopy.h" #include "curlx/version_win32.h" #include "curlx/strerr.h" #include "curlx/strparse.h" @@ -100,9 +101,9 @@ defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \ defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD) #if defined(USE_WINSOCK) || \ - (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ - (defined(__DragonFly__) && __DragonFly_version < 500702) || \ - (defined(_WIN32) && !defined(TCP_KEEPIDLE)) + (defined(__sun) && !defined(TCP_KEEPIDLE)) || \ + (defined(__DragonFly__) && __DragonFly_version < 500702) || \ + (defined(_WIN32) && !defined(TCP_KEEPIDLE)) /* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299 * use millisecond units. */ #define KEEPALIVE_FACTOR(x) ((x) *= 1000) @@ -236,8 +237,15 @@ * Note that the consequent probes will not be sent * at equal intervals on Solaris, but will be sent * using the exponential backoff algorithm. */ - optval = curlx_sltosi(data->set.tcp_keepcnt) * - curlx_sltosi(data->set.tcp_keepintvl); + { + int keepcnt = curlx_sltosi(data->set.tcp_keepcnt); + int keepintvl = curlx_sltosi(data->set.tcp_keepintvl); + + if(keepcnt > 0 && keepintvl > (INT_MAX / keepcnt)) + optval = INT_MAX; + else + optval = keepcnt * keepintvl; + } KEEPALIVE_FACTOR(optval); if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, (void *)&optval, sizeof(optval)) < 0) { @@ -258,42 +266,30 @@ } /** - * Assign the address `ai` to the Curl_sockaddr_ex `dest` and - * set the transport used. + * Assign the addrinfo `ai` to the Curl_sockaddr_ex `addr` with + * transport determining socktype and protocol. */ -static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest, - const struct Curl_addrinfo *ai, - uint8_t transport) +CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr, + const struct Curl_addrinfo *ai, + uint8_t transport) { /* - * The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr - * structure with enough space available to directly hold any - * protocol-specific address structures. The variable declared here will be - * used to pass / receive data to/from the fopensocket callback if this has - * been set, before that, it is initialized from parameters. + * The Curl_sockaddr_ex structure is libcurl's external API + * curl_sockaddr structure with enough space available to directly hold + * any protocol-specific address structures. The variable declared here + * will be used to pass / receive data to/from the fopensocket callback + * if this has been set, before that, it is initialized from parameters. */ - dest->family = ai->ai_family; - switch(transport) { - case TRNSPRT_TCP: - dest->socktype = SOCK_STREAM; - dest->protocol = IPPROTO_TCP; - break; - case TRNSPRT_UNIX: - dest->socktype = SOCK_STREAM; - dest->protocol = IPPROTO_IP; - break; - default: /* UDP and QUIC */ - dest->socktype = SOCK_DGRAM; - dest->protocol = IPPROTO_UDP; - break; - } - dest->addrlen = (unsigned int)ai->ai_addrlen; + addr->family = ai->ai_family; + addr->socktype = Curl_socktype_for_transport(transport); + addr->protocol = Curl_protocol_for_transport(transport); + addr->addrlen = (unsigned int)ai->ai_addrlen; - DEBUGASSERT(dest->addrlen <= sizeof(dest->curl_sa_addrbuf)); - if(dest->addrlen > sizeof(dest->curl_sa_addrbuf)) + DEBUGASSERT(addr->addrlen <= sizeof(addr->curl_sa_addrbuf)); + if(addr->addrlen > sizeof(addr->curl_sa_addrbuf)) return CURLE_TOO_LARGE; - memcpy(&dest->curl_sa_addrbuf, ai->ai_addr, dest->addrlen); + memcpy(&addr->curl_sa_addrbuf, ai->ai_addr, addr->addrlen); return CURLE_OK; } @@ -405,7 +401,7 @@ /* if the caller does not want info back, use a local temp copy */ addr = &dummy; - result = sock_assign_addr(addr, ai, transport); + result = Curl_socket_addr_from_ai(addr, ai, transport); if(result) return result; @@ -530,7 +526,8 @@ #ifndef CURL_DISABLE_BINDLOCAL static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, - curl_socket_t sockfd, int af, unsigned int scope) + curl_socket_t sockfd, int af, unsigned int scope, + uint8_t transport) { struct Curl_sockaddr_storage sa; struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ @@ -641,21 +638,21 @@ * of the connection. The resolve functions should really be changed * to take a type parameter instead. */ - int ip_version = (af == AF_INET) ? - CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER; + uint8_t dns_queries = (af == AF_INET) ? + CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA); #ifdef USE_IPV6 if(af == AF_INET6) - ip_version = CURL_IPRESOLVE_V6; + dns_queries = CURL_DNSQ_AAAA; #endif - (void)Curl_resolv_blocking(data, host, 80, ip_version, &h); + (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h); if(h) { int h_af = h->addr->ai_family; /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ Curl_printable_address(h->addr, myhost, sizeof(myhost)); infof(data, "Name '%s' family %i resolved to '%s' family %i", host, af, myhost, h_af); - Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */ + Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */ if(af != h_af) { /* bad IP version combo, signal the caller to try another address family if available */ @@ -688,8 +685,8 @@ if(scope_ptr) { /* The "myhost" string either comes from Curl_if2ip or from Curl_printable_address. The latter returns only numeric scope - IDs and the former returns none at all. So the scope ID, if - present, is known to be numeric */ + IDs and the former returns none at all. Making the scope ID, + if present, known to be numeric */ curl_off_t scope_id; if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr), &scope_id, UINT_MAX)) @@ -892,18 +889,13 @@ }; static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { - CURLcode result; - memset(ctx, 0, sizeof(*ctx)); ctx->sock = CURL_SOCKET_BAD; ctx->transport = transport; - - result = sock_assign_addr(&ctx->addr, ai, transport); - if(result) - return result; + ctx->addr = *addr; #ifdef DEBUGBUILD { @@ -934,7 +926,7 @@ } #endif - return result; + return CURLE_OK; } static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -1143,7 +1135,8 @@ #endif ) { result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family, - Curl_ipv6_scope(&ctx->addr.curl_sa_addr)); + Curl_ipv6_scope(&ctx->addr.curl_sa_addr), + ctx->transport); if(result) { if(result == CURLE_UNSUPPORTED_PROTOCOL) { /* The address family is not supported on this interface. @@ -1313,26 +1306,31 @@ } } else if(rc & CURL_CSELECT_ERR) { + CURL_TRC_CF(data, cf, "poll/select error on fd=%" FMT_SOCKET_T, ctx->sock); (void)verifyconnect(ctx->sock, &ctx->error); result = CURLE_COULDNT_CONNECT; } out: if(result) { + VERBOSE(char buffer[STRERROR_LEN]); + set_local_ip(cf, data); if(ctx->error) { - VERBOSE(char buffer[STRERROR_LEN]); - set_local_ip(cf, data); data->state.os_errno = ctx->error; SET_SOCKERRNO(ctx->error); - infof(data, "connect to %s port %u from %s port %d failed: %s", - ctx->ip.remote_ip, ctx->ip.remote_port, - ctx->ip.local_ip, ctx->ip.local_port, - curlx_strerror(ctx->error, buffer, sizeof(buffer))); + VERBOSE(curlx_strerror(ctx->error, buffer, sizeof(buffer))); + } + else { + VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed"))); } if(ctx->sock != CURL_SOCKET_BAD) { socket_close(data, cf->conn, TRUE, ctx->sock); ctx->sock = CURL_SOCKET_BAD; } + infof(data, "connect to %s port %u from %s port %d failed: %s", + ctx->ip.remote_ip, ctx->ip.remote_port, + ctx->ip.local_ip, ctx->ip.local_port, + curlx_strerror(ctx->error, buffer, sizeof(buffer))); *done = FALSE; } return result; @@ -1713,7 +1711,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { struct cf_socket_ctx *ctx = NULL; @@ -1723,7 +1721,7 @@ (void)data; (void)conn; DEBUGASSERT(transport == TRNSPRT_TCP); - if(!ai) { + if(!addr) { result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } @@ -1734,7 +1732,7 @@ goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1743,8 +1741,8 @@ out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; @@ -1879,7 +1877,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { struct cf_socket_ctx *ctx = NULL; @@ -1895,7 +1893,7 @@ goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1904,8 +1902,8 @@ out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; @@ -1933,7 +1931,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { struct cf_socket_ctx *ctx = NULL; @@ -1949,7 +1947,7 @@ goto out; } - result = cf_socket_ctx_init(ctx, ai, transport); + result = cf_socket_ctx_init(ctx, addr, transport); if(result) goto out; @@ -1958,8 +1956,8 @@ out: *pcf = (!result) ? cf : NULL; if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; @@ -2195,8 +2193,8 @@ out: if(result) { - Curl_safefree(cf); - Curl_safefree(ctx); + curlx_safefree(cf); + curlx_safefree(ctx); } return result; }
diff --git a/Utilities/cmcurl/lib/cf-socket.h b/Utilities/cmcurl/lib/cf-socket.h index 014f5f7..40c001c 100644 --- a/Utilities/cmcurl/lib/cf-socket.h +++ b/Utilities/cmcurl/lib/cf-socket.h
@@ -60,6 +60,10 @@ CURLcode Curl_parse_interface(const char *input, char **dev, char **iface, char **host); +CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr, + const struct Curl_addrinfo *ai, + uint8_t transport); + /* * Create a socket based on info from 'conn' and 'ai'. * @@ -91,7 +95,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); /** @@ -104,7 +108,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); /** @@ -117,7 +121,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); /**
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c index 27b0306..6d7d8ef 100644 --- a/Utilities/cmcurl/lib/cfilters.c +++ b/Utilities/cmcurl/lib/cfilters.c
@@ -34,8 +34,9 @@ #include "curlx/strparse.h" #ifdef UNITTESTS -/* used by unit2600.c */ -void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) +/* @unittest 2600 */ +UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); +UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data) { cf->connected = FALSE; if(cf->next) @@ -115,6 +116,37 @@ CURLE_UNKNOWN_OPTION; } +#ifdef CURLVERBOSE +static void conn_trc_filters(struct Curl_easy *data, + int sockindex, + const char *info) +{ + if(CURL_TRC_M_is_verbose(data) && data->conn) { + struct Curl_cfilter *cf = data->conn->cfilter[sockindex]; + + if(cf) { + struct dynbuf msg; + CURLcode result = CURLE_OK; + + curlx_dyn_init(&msg, 1024); + result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex); + for(; cf && !result; cf = cf->next) { + result = curlx_dyn_addf(&msg, "[%s%s]", + cf->connected ? "" : "!", cf->cft->name); + } + if(!result) + CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg)); + else + CURL_TRC_M(data, "%s [%d] error %d tracing chain", + info, sockindex, result); + curlx_dyn_free(&msg); + } + else + CURL_TRC_M(data, "%s [%d][-]", info, sockindex); + } +} +#endif /* CURLVERBOSE */ + void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf, struct Curl_easy *data) { @@ -488,6 +520,24 @@ } } +static void conn_remove_setup_filters(struct Curl_easy *data, + int sockindex) +{ + struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex]; + while(*anchor) { + struct Curl_cfilter *cf = *anchor; + if(cf->connected && (cf->cft->flags & CF_TYPE_SETUP)) { + *anchor = cf->next; + cf->next = NULL; + CURL_TRC_CF(data, cf, "removing connected setup filter"); + cf->cft->destroy(cf, data); + curlx_free(cf); + } + else + anchor = &cf->next; + } +} + CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, bool blocking, @@ -505,6 +555,11 @@ if(!CONN_SOCK_IDX_VALID(sockindex)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(data->conn->scheme->flags & PROTOPT_NONETWORK) { + *done = TRUE; + return CURLE_OK; + } + cf = data->conn->cfilter[sockindex]; if(!cf) { *done = FALSE; @@ -536,10 +591,14 @@ conn_report_connect_stats(cf, data); data->conn->keepalive = *Curl_pgrs_now(data); VERBOSE(result = cf_verboseconnect(data, cf)); + VERBOSE(conn_trc_filters(data, sockindex, "connected")); + conn_remove_setup_filters(data, sockindex); + VERBOSE(conn_trc_filters(data, sockindex, "reduced to")); goto out; } else if(result) { CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result); + VERBOSE(conn_trc_filters(data, sockindex, "failed to connect")); conn_report_connect_stats(cf, data); goto out; } @@ -649,14 +708,16 @@ bool Curl_conn_get_ssl_info(struct Curl_easy *data, struct connectdata *conn, int sockindex, + int query, struct curl_tlssessioninfo *info) { if(!CONN_SOCK_IDX_VALID(sockindex)) return FALSE; if(Curl_conn_is_ssl(conn, sockindex)) { struct Curl_cfilter *cf = conn->cfilter[sockindex]; - CURLcode result = cf ? cf->cft->query(cf, data, CF_QUERY_SSL_INFO, - NULL, (void *)info) : CURLE_UNKNOWN_OPTION; + CURLcode result = cf ? + cf->cft->query(cf, data, query, NULL, (void *)info) : + CURLE_UNKNOWN_OPTION; return !result; } return FALSE; @@ -697,6 +758,41 @@ return Curl_conn_cf_get_transport(cf, data); } +int Curl_socktype_for_transport(uint8_t transport) +{ + switch(transport) { + case TRNSPRT_TCP: + return SOCK_STREAM; + case TRNSPRT_UNIX: + return SOCK_STREAM; + default: /* UDP and QUIC */ + return SOCK_DGRAM; + } +} + +int Curl_protocol_for_transport(uint8_t transport) +{ + switch(transport) { + case TRNSPRT_TCP: + return IPPROTO_TCP; + case TRNSPRT_UNIX: + return IPPROTO_IP; + default: /* UDP and QUIC */ + return IPPROTO_UDP; + } +} + +bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)data; + for(; cf; cf = cf->next) { + if(cf->cft->flags & CF_TYPE_HTTPSRR) + return TRUE; + } + return FALSE; +} + const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, struct connectdata *conn) { @@ -800,12 +896,19 @@ return result; } +/* + * Return values: + * -1 = error + * 0 = timeout + * N = number of structures with non zero revent fields + */ int Curl_conn_cf_poll(struct Curl_cfilter *cf, struct Curl_easy *data, timediff_t timeout_ms) { struct easy_pollset ps; - int result; + int rc; + CURLcode result; DEBUGASSERT(cf); DEBUGASSERT(data); @@ -814,20 +917,24 @@ result = Curl_conn_cf_adjust_pollset(cf, data, &ps); if(!result) - result = Curl_pollset_poll(data, &ps, timeout_ms); + rc = Curl_pollset_poll(data, &ps, timeout_ms); + else + rc = -1; Curl_pollset_cleanup(&ps); - return result; + return rc; } void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, const char **phost, int *pport) { struct Curl_cfilter *cf, *cf_proxy = NULL; + int portarg = -1; if(!data->conn) { DEBUGASSERT(0); *phost = ""; - *pport = -1; + if(pport) + *pport = -1; return; } @@ -843,12 +950,14 @@ * to an interim host and any authentication or other things apply * to this interim host and port. */ if(!cf_proxy || cf_proxy->cft->query(cf_proxy, data, CF_QUERY_HOST_PORT, - pport, CURL_UNCONST(phost))) { + &portarg, CURL_UNCONST(phost))) { /* Everything connected or query unsuccessful, the overall * connection's destination is the answer */ *phost = data->conn->host.name; - *pport = data->conn->remote_port; + portarg = data->conn->remote_port; } + if(pport) + *pport = portarg; } CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, @@ -1029,7 +1138,7 @@ result = cf ? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT, &n, NULL) : CURLE_UNKNOWN_OPTION; /* If no filter answered the query, the default is a non-multiplexed - * connection with limit 1. Otherwise, the the query may return 0 + * connection with limit 1. Otherwise, the query may return 0 * for connections that are in shutdown, e.g. server HTTP/2 GOAWAY. */ return (result || n < 0) ? 1 : (size_t)n; }
diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h index c754634..ac56737 100644 --- a/Utilities/cmcurl/lib/cfilters.h +++ b/Utilities/cmcurl/lib/cfilters.h
@@ -199,12 +199,17 @@ * CF_TYPE_MULTIPLEX: provides multiplexing of easy handles * CF_TYPE_PROXY provides proxying * CF_TYPE_HTTP implement a version of the HTTP protocol + * CF_TYPE_SETUP filter is only needed for connection setup and + * can be removed once connected + * CF_TYPE_HTTPSRR filter that wants HTTPS-RR information */ #define CF_TYPE_IP_CONNECT (1 << 0) #define CF_TYPE_SSL (1 << 1) #define CF_TYPE_MULTIPLEX (1 << 2) #define CF_TYPE_PROXY (1 << 3) #define CF_TYPE_HTTP (1 << 4) +#define CF_TYPE_SETUP (1 << 5) +#define CF_TYPE_HTTPSRR (1 << 6) /* A connection filter type, e.g. specific implementation. */ struct Curl_cftype { @@ -345,9 +350,16 @@ unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, struct Curl_easy *data); +int Curl_socktype_for_transport(uint8_t transport); +int Curl_protocol_for_transport(uint8_t transport); + const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf, struct Curl_easy *data); +/* The filter (or one of its sub-filters) wants HTTPS-RR information. */ +bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, + struct Curl_easy *data); + #define CURL_CF_SSL_DEFAULT (-1) #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 @@ -388,12 +400,13 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); /* - * Fill `info` with information about the TLS instance securing - * the connection when available, otherwise e.g. when - * Curl_conn_is_ssl() is FALSE, return FALSE. + * Fill `info` with information about the TLS instance securing the connection + * when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return + * FALSE. 'query' should be CF_QUERY_SSL_INFO or CF_QUERY_SSL_CTX_INFO. */ bool Curl_conn_get_ssl_info(struct Curl_easy *data, struct connectdata *conn, int sockindex, + int query, struct curl_tlssessioninfo *info); CURLcode Curl_conn_get_ip_info(struct Curl_easy *data, @@ -429,7 +442,7 @@ /** * Shutdown the connection at `sockindex` non-blocking, using timeout * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS. - * Will return CURLE_OK and *done == FALSE if not finished. + * Return CURLE_OK and *done == FALSE if not finished. */ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done); @@ -565,10 +578,6 @@ struct connectdata *conn, int sockindex); -#ifdef UNITTESTS -void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); -#endif - /** * Get the remote hostname and port that the connection is currently * talking to (or will talk to). @@ -604,14 +613,14 @@ /* * Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. - * Will return CURLE_AGAIN iff blocked on receiving. + * Return CURLE_AGAIN iff blocked on receiving. */ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, char *buf, size_t len, size_t *pnread); /* * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. - * Will return CURLE_AGAIN iff blocked on sending. + * Return CURLE_AGAIN iff blocked on sending. */ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, const void *buf, size_t len, bool eos,
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c index a865959..33fb68b 100644 --- a/Utilities/cmcurl/lib/conncache.c +++ b/Utilities/cmcurl/lib/conncache.c
@@ -206,7 +206,7 @@ /* treat the connection as aborted in CONNECT_ONLY situations, we do * not know what the APP did with it. */ - if(conn->connect_only) + if(conn->bits.connect_only) aborted = TRUE; conn->bits.aborted = aborted; @@ -354,7 +354,7 @@ for(curr = Curl_llist_head(&bundle->conns); curr; curr = Curl_node_next(curr)) { conn = Curl_node_elem(curr); - if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only) + if(CONN_INUSE(conn) || conn->bits.close || conn->bits.connect_only) continue; /* Set higher score for the age passed since the connection was used */ score = curlx_ptimediff_ms(pnow, &conn->lastused); @@ -375,7 +375,7 @@ size_t dest_limit = 0; size_t total_limit = 0; size_t shutdowns; - int result = CPOOL_LIMIT_OK; + int res = CPOOL_LIMIT_OK; if(!cpool) return CPOOL_LIMIT_OK; @@ -425,7 +425,7 @@ shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination); } if((live + shutdowns) >= dest_limit) { - result = CPOOL_LIMIT_DEST; + res = CPOOL_LIMIT_DEST; goto out; } } @@ -453,14 +453,14 @@ shutdowns = Curl_cshutdn_count(cpool->idata); } if((cpool->num_conn + shutdowns) >= total_limit) { - result = CPOOL_LIMIT_TOTAL; + res = CPOOL_LIMIT_TOTAL; goto out; } } out: CPOOL_UNLOCK(cpool, cpool->idata); - return result; + return res; } CURLcode Curl_cpool_add(struct Curl_easy *data, @@ -558,7 +558,7 @@ struct cpool *cpool = cpool_get_instance(data); bool kept = TRUE; - if(!data) + if(!data || !data->multi) return kept; if(!data->multi->maxconnects) { @@ -600,7 +600,7 @@ { struct cpool *cpool = cpool_get_instance(data); struct cpool_bundle *bundle; - bool result = FALSE; + bool found = FALSE; DEBUGASSERT(cpool); DEBUGASSERT(conn_cb); @@ -619,17 +619,17 @@ curr = Curl_node_next(curr); if(conn_cb(conn, userdata)) { - result = TRUE; + found = TRUE; break; } } } if(done_cb) { - result = done_cb(result, userdata); + found = done_cb(userdata); } CPOOL_UNLOCK(cpool, data); - return result; + return found; } void Curl_conn_terminate(struct Curl_easy *data, @@ -665,7 +665,7 @@ /* treat the connection as aborted in CONNECT_ONLY situations, * so no graceful shutdown is attempted. */ - if(conn->connect_only) + if(conn->bits.connect_only) aborted = TRUE; if(data->multi) {
diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h index d8626ae..7cee4d4 100644 --- a/Utilities/cmcurl/lib/conncache.h +++ b/Utilities/cmcurl/lib/conncache.h
@@ -84,8 +84,8 @@ /** * Return if the pool has reached its configured limits for adding - * the given connection. Will try to discard the oldest, idle - * connections to make space. + * the given connection. Try to discard the oldest, idle connections + * to make space. */ #define CPOOL_LIMIT_OK 0 #define CPOOL_LIMIT_DEST 1 @@ -98,7 +98,7 @@ void *userdata); /* Act on the result of the find, may override it. */ -typedef bool Curl_cpool_done_match_cb(bool result, void *userdata); +typedef bool Curl_cpool_done_match_cb(void *userdata); /** * Find a connection in the pool matching `destination`.
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c index 78c9370..b13d496 100644 --- a/Utilities/cmcurl/lib/connect.c +++ b/Utilities/cmcurl/lib/connect.c
@@ -54,6 +54,7 @@ #include "strerror.h" #include "cfilters.h" #include "connect.h" +#include "cf-dns.h" #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" @@ -96,14 +97,17 @@ #endif /* - * Curl_timeleft_ms() returns the amount of milliseconds left allowed for the + * timeleft_now_ms() returns the amount of milliseconds left allowed for the * transfer/connection. If the value is 0, there is no timeout (ie there is * infinite time left). If the value is negative, the timeout time has already * elapsed. - * @unittest: 1303 + * + * @unittest 1303 */ -timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, - const struct curltime *pnow) +UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data, + const struct curltime *pnow); +UNITTEST timediff_t timeleft_now_ms(struct Curl_easy *data, + const struct curltime *pnow) { timediff_t timeleft_ms = 0; timediff_t ctimeleft_ms = 0; @@ -138,7 +142,7 @@ timediff_t Curl_timeleft_ms(struct Curl_easy *data) { - return Curl_timeleft_now_ms(data, Curl_pgrs_now(data)); + return timeleft_now_ms(data, Curl_pgrs_now(data)); } void Curl_shutdown_start(struct Curl_easy *data, int sockindex, @@ -343,7 +347,6 @@ { struct cf_setup_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - struct Curl_dns_entry *dns = data->state.dns[cf->sockindex]; if(cf->connected) { *done = TRUE; @@ -352,8 +355,6 @@ /* connect current sub-chain */ connect_sub_chain: - if(!dns) - return CURLE_FAILED_INIT; if(cf->next && !cf->next->connected) { result = Curl_conn_cf_connect(cf->next, data, done); @@ -373,7 +374,27 @@ /* sub-chain connected, do we need to add more? */ #ifndef CURL_DISABLE_PROXY if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - result = Curl_cf_socks_proxy_insert_after(cf, data); + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + const char *hostname = + cf->conn->bits.httpproxy ? + cf->conn->http_proxy.host.name : + cf->conn->bits.conn_to_host ? + cf->conn->conn_to_host.name : + cf->sockindex == SECONDARYSOCKET ? + cf->conn->secondaryhostname : cf->conn->host.name; + uint16_t port = + cf->conn->bits.httpproxy ? cf->conn->http_proxy.port : + cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port : + cf->conn->bits.conn_to_port ? cf->conn->conn_to_port : + cf->conn->remote_port; + const char *user = cf->conn->socks_proxy.user; + const char *passwd = cf->conn->socks_proxy.passwd; + + result = Curl_cf_socks_proxy_insert_after( + cf, data, hostname, port, cf->conn->ip_version, + cf->conn->socks_proxy.proxytype, user, passwd); if(result) return result; ctx->state = CF_SETUP_CNNCT_SOCKS; @@ -408,7 +429,7 @@ #ifndef CURL_DISABLE_PROXY if(data->set.haproxyprotocol) { if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { - failf(data, "haproxy protocol not support with SSL " + failf(data, "haproxy protocol not supported with SSL " "encryption in place (QUIC?)"); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -464,12 +485,12 @@ struct cf_setup_ctx *ctx = cf->ctx; CURL_TRC_CF(data, cf, "destroy"); - Curl_safefree(ctx); + curlx_safefree(ctx); } struct Curl_cftype Curl_cft_setup = { "SETUP", - 0, + CF_TYPE_SETUP, CURL_LOG_LVL_NONE, cf_setup_destroy, cf_setup_connect, @@ -559,13 +580,11 @@ int ssl_mode) { CURLcode result = CURLE_OK; + uint8_t dns_queries; DEBUGASSERT(data); DEBUGASSERT(conn->scheme); - DEBUGASSERT(dns); - - Curl_resolv_unlink(data, &data->state.dns[sockindex]); - data->state.dns[sockindex] = dns; + DEBUGASSERT(!conn->cfilter[sockindex]); #ifndef CURL_DISABLE_HTTP if(!conn->cfilter[sockindex] && @@ -585,13 +604,34 @@ goto out; } + dns_queries = Curl_resolv_dns_queries(data, conn->ip_version); +#ifdef USE_HTTPSRR + if(sockindex == FIRSTSOCKET) + dns_queries |= CURL_DNSQ_HTTPS; +#endif + result = Curl_cf_dns_add(data, conn, sockindex, dns_queries, + conn->transport_wanted, dns); DEBUGASSERT(conn->cfilter[sockindex]); out: - if(result) - Curl_resolv_unlink(data, &data->state.dns[sockindex]); return result; } +#ifdef USE_UNIX_SOCKETS +const char *Curl_conn_get_unix_path(struct connectdata *conn) +{ + const char *unix_path = conn->unix_domain_socket; + +#ifndef CURL_DISABLE_PROXY + if(!unix_path && conn->bits.proxy && conn->socks_proxy.host.name && + !strncmp(UNIX_SOCKET_PREFIX "/", + conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) + unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; +#endif + + return unix_path; +} +#endif /* USE_UNIX_SOCKETS */ + void Curl_conn_set_multiplex(struct connectdata *conn) { if(!conn->bits.multiplex) {
diff --git a/Utilities/cmcurl/lib/connect.h b/Utilities/cmcurl/lib/connect.h index 071b432..40f1c9c 100644 --- a/Utilities/cmcurl/lib/connect.h +++ b/Utilities/cmcurl/lib/connect.h
@@ -25,6 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" +#include "hostip.h" #include "curlx/timeval.h" struct Curl_dns_entry; @@ -37,8 +38,6 @@ /* generic function that returns how much time there is left to run, according to the timeouts set */ timediff_t Curl_timeleft_ms(struct Curl_easy *data); -timediff_t Curl_timeleft_now_ms(struct Curl_easy *data, - const struct curltime *pnow); #define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */ @@ -127,6 +126,15 @@ /* Set conn to allow multiplexing. */ void Curl_conn_set_multiplex(struct connectdata *conn); +#ifdef USE_UNIX_SOCKETS +#ifndef CURL_DISABLE_PROXY +#define UNIX_SOCKET_PREFIX "localhost" +#endif +const char *Curl_conn_get_unix_path(struct connectdata *conn); +#else +#define Curl_conn_get_unix_path(c) NULL +#endif + extern struct Curl_cftype Curl_cft_setup; #endif /* HEADER_CURL_CONNECT_H */
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c index b8dbd7d..dea9a0f 100644 --- a/Utilities/cmcurl/lib/content_encoding.c +++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -31,13 +31,13 @@ #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG /* Ignore -Wvla warnings in brotli headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvla" #endif #include <brotli/decode.h> -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif #endif @@ -751,7 +751,7 @@ } if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) { - failf(data, "Reject response due to more than %u content encodings", + failf(data, "Reject response due to more than %d content encodings", MAX_ENCODE_STACK); return CURLE_BAD_CONTENT_ENCODING; }
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c index 4d53cc4..0f822ea 100644 --- a/Utilities/cmcurl/lib/cookie.c +++ b/Utilities/cmcurl/lib/cookie.c
@@ -347,7 +347,7 @@ cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E - But Firefox and Chrome as of June 2022 accept space, comma and double-quotes + Yet, Firefox and Chrome as of June 2022 accept space, comma and double-quotes fine. The prime reason for filtering out control bytes is that some HTTP servers return 400 for requests that contain such. */ @@ -423,18 +423,17 @@ /* this function return errors on OOM etc, not on plain cookie format problems */ -static CURLcode -parse_cookie_header(struct Curl_easy *data, - struct Cookie *co, - struct CookieInfo *ci, - bool *okay, /* if the cookie was fine */ - const char *ptr, - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is - set, used to get default path for - the cookie unless set */ - bool secure) /* TRUE if connection is over secure - origin */ +static CURLcode parse_cookie_header( + struct Curl_easy *data, + struct Cookie *co, + struct CookieInfo *ci, + bool *okay, /* if the cookie was fine */ + const char *ptr, + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is + set, used to get default path for + the cookie unless set */ + bool secure) /* TRUE if connection is over secure origin */ { /* This line was read off an HTTP-header */ time_t now = 0; @@ -461,6 +460,13 @@ sep = TRUE; /* a '=' was used */ if(!curlx_str_cspn(&ptr, &val, ";\r\n")) curlx_str_trimblanks(&val); + + /* Reject cookies with a TAB inside the value */ + if(curlx_strlen(&val) && + memchr(curlx_str(&val), '\t', curlx_strlen(&val))) { + infof(data, "cookie contains TAB, dropping"); + return CURLE_OK; + } } else curlx_str_init(&val); @@ -489,13 +495,6 @@ return CURLE_OK; } - /* Reject cookies with a TAB inside the value */ - if(curlx_strlen(&val) && - memchr(curlx_str(&val), '\t', curlx_strlen(&val))) { - infof(data, "cookie contains TAB, dropping"); - return CURLE_OK; - } - /* Check if we have a reserved prefix set. */ if(!strncmp("__Secure-", curlx_str(&name), 9)) co->prefix_secure = TRUE; @@ -930,17 +929,16 @@ * IPv6 address. * */ -CURLcode -Curl_cookie_add(struct Curl_easy *data, - struct CookieInfo *ci, - bool httpheader, /* TRUE if HTTP header-style line */ - bool noexpire, /* if TRUE, skip remove_expired() */ - const char *lineptr, /* first character of the line */ - const char *domain, /* default domain */ - const char *path, /* full path used when this cookie is set, - used to get default path for the cookie - unless set */ - bool secure) /* TRUE if connection is over secure origin */ +CURLcode Curl_cookie_add( + struct Curl_easy *data, + struct CookieInfo *ci, + bool httpheader, /* TRUE if HTTP header-style line */ + bool noexpire, /* if TRUE, skip remove_expired() */ + const char *lineptr, /* first character of the line */ + const char *domain, /* default domain */ + const char *path, /* full path used when this cookie is set, used + to get default path for the cookie unless set */ + bool secure) /* TRUE if connection is over secure origin */ { struct Cookie comem; struct Cookie *co; @@ -1106,8 +1104,17 @@ fp = curlx_fopen(file, "rb"); if(!fp) infof(data, "WARNING: failed to open cookie file \"%s\"", file); - else - handle = fp; + else { + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) != -1) && S_ISDIR(stat.st_mode)) { + curlx_fclose(fp); + fp = NULL; + infof(data, "WARNING: cookie filename points to a directory: \"%s\"", + file); + } + else + handle = fp; + } } } @@ -1464,7 +1471,7 @@ FILE *out = NULL; bool use_stdout = FALSE; char *tempstore = NULL; - CURLcode error = CURLE_OK; + CURLcode result = CURLE_OK; if(!ci) /* no cookie engine alive */ @@ -1479,8 +1486,8 @@ use_stdout = TRUE; } else { - error = Curl_fopen(data, filename, &out, &tempstore); - if(error) + result = Curl_fopen(data, filename, &out, &tempstore); + if(result) goto error; } @@ -1497,7 +1504,7 @@ array = curlx_calloc(1, sizeof(struct Cookie *) * ci->numcookies); if(!array) { - error = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto error; } @@ -1517,7 +1524,7 @@ char *format_ptr = get_netscape_format(array[i]); if(!format_ptr) { curlx_free(array); - error = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; goto error; } curl_mfprintf(out, "%s\n", format_ptr); @@ -1531,7 +1538,7 @@ curlx_fclose(out); out = NULL; if(tempstore && curlx_rename(tempstore, filename)) { - error = CURLE_WRITE_ERROR; + result = CURLE_WRITE_ERROR; goto error; } } @@ -1551,7 +1558,7 @@ unlink(tempstore); curlx_free(tempstore); } - return error; + return result; } static struct curl_slist *cookie_list(struct Curl_easy *data)
diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h index fabd04d..f66e0ef 100644 --- a/Utilities/cmcurl/lib/cookie.h +++ b/Utilities/cmcurl/lib/cookie.h
@@ -125,11 +125,11 @@ void Curl_cookie_clearsess(struct CookieInfo *ci); #if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES) -#define Curl_cookie_list(x) NULL +#define Curl_cookie_list(x) NULL #define Curl_cookie_loadfiles(x) CURLE_OK -#define Curl_cookie_init() NULL -#define Curl_cookie_run(x) Curl_nop_stmt -#define Curl_cookie_cleanup(x) Curl_nop_stmt +#define Curl_cookie_init() NULL +#define Curl_cookie_run(x) Curl_nop_stmt +#define Curl_cookie_cleanup(x) Curl_nop_stmt #define Curl_flush_cookies(x, y) Curl_nop_stmt #else void Curl_flush_cookies(struct Curl_easy *data, bool cleanup);
diff --git a/Utilities/cmcurl/lib/cshutdn.c b/Utilities/cmcurl/lib/cshutdn.c index 308f2aa..27b4a9f 100644 --- a/Utilities/cmcurl/lib/cshutdn.c +++ b/Utilities/cmcurl/lib/cshutdn.c
@@ -87,14 +87,14 @@ return; } - if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) + if(!conn->bits.connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET)) r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1); else { r1 = CURLE_OK; done1 = TRUE; } - if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) + if(!conn->bits.connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET)) r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2); else { r2 = CURLE_OK;
diff --git a/Utilities/cmcurl/lib/cshutdn.h b/Utilities/cmcurl/lib/cshutdn.h index 9030474..b2e83f3 100644 --- a/Utilities/cmcurl/lib/cshutdn.h +++ b/Utilities/cmcurl/lib/cshutdn.h
@@ -33,7 +33,7 @@ struct Curl_sigpipe_ctx; /* Run the shutdown of the connection once. - * Will shortly attach/detach `data` to `conn` while doing so. + * Shortly attach/detach `data` to `conn` while doing so. * `done` will be set TRUE if any error was encountered or if * the connection was shut down completely. */ void Curl_cshutdn_run_once(struct Curl_easy *data, @@ -78,7 +78,7 @@ bool Curl_cshutdn_close_oldest(struct Curl_easy *data, const char *destination); -/* Add a connection to have it shut down. Will terminate the oldest +/* Add a connection to have it shut down. Terminate the oldest * connection when total connection limit of multi is being reached. */ void Curl_cshutdn_add(struct cshutdn *cshutdn, struct connectdata *conn,
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.c b/Utilities/cmcurl/lib/curl_addrinfo.c index 43d398d..fd26c5f 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.c +++ b/Utilities/cmcurl/lib/curl_addrinfo.c
@@ -404,7 +404,7 @@ * Given an IPv4 or IPv6 dotted string address, this converts it to a proper * allocated Curl_addrinfo struct and returns it. */ -CURLcode Curl_str2addr(const char *dotted, int port, +CURLcode Curl_str2addr(const char *dotted, uint16_t port, struct Curl_addrinfo **addrp) { struct in_addr in; @@ -422,10 +422,15 @@ return CURLE_BAD_FUNCTION_ARGUMENT; /* bad input format */ } -bool Curl_is_ipaddr(const char *address) +bool Curl_is_ipv4addr(const char *address) { struct in_addr in; - if(curlx_inet_pton(AF_INET, address, &in) > 0) + return (curlx_inet_pton(AF_INET, address, &in) > 0); +} + +bool Curl_is_ipaddr(const char *address) +{ + if(Curl_is_ipv4addr(address)) return TRUE; #ifdef USE_IPV6 { @@ -442,34 +447,31 @@ /** * Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo * struct initialized with this path. - * Set '*longpath' to TRUE if the error is a too long path. + * Returns CURLE_TOO_LARGE when path is too long. */ -struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, - bool abstract) +CURLcode Curl_unix2addr(const char *path, bool abstract, + struct Curl_addrinfo **paddr) { struct Curl_addrinfo *ai; struct sockaddr_un *sa_un; size_t path_len; - *longpath = FALSE; + *paddr = NULL; + + /* sun_path must be able to store the null-terminated path */ + path_len = strlen(path) + 1; + if(path_len > sizeof(sa_un->sun_path)) + return CURLE_TOO_LARGE; ai = curlx_calloc(1, sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un)); if(!ai) - return NULL; - ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); + return CURLE_OUT_OF_MEMORY; + ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo)); sa_un = (void *)ai->ai_addr; sa_un->sun_family = AF_UNIX; - /* sun_path must be able to store the null-terminated path */ - path_len = strlen(path) + 1; - if(path_len > sizeof(sa_un->sun_path)) { - curlx_free(ai); - *longpath = TRUE; - return NULL; - } - ai->ai_family = AF_UNIX; ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */ ai->ai_addrlen = (curl_socklen_t) @@ -481,7 +483,8 @@ else memcpy(sa_un->sun_path, path, path_len); /* copy NUL byte */ - return ai; + *paddr = ai; + return CURLE_OK; } #endif
diff --git a/Utilities/cmcurl/lib/curl_addrinfo.h b/Utilities/cmcurl/lib/curl_addrinfo.h index f464b5e..da2da87 100644 --- a/Utilities/cmcurl/lib/curl_addrinfo.h +++ b/Utilities/cmcurl/lib/curl_addrinfo.h
@@ -71,13 +71,14 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port); #endif +bool Curl_is_ipv4addr(const char *address); bool Curl_is_ipaddr(const char *address); -CURLcode Curl_str2addr(const char *dotted, int port, +CURLcode Curl_str2addr(const char *dotted, uint16_t port, struct Curl_addrinfo **addrp); #ifdef USE_UNIX_SOCKETS -struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath, - bool abstract); +CURLcode Curl_unix2addr(const char *path, bool abstract, + struct Curl_addrinfo **paddr); #endif #if defined(CURL_MEMDEBUG) && defined(HAVE_GETADDRINFO) && \
diff --git a/Utilities/cmcurl/lib/curl_config-cmake.h.in b/Utilities/cmcurl/lib/curl_config-cmake.h.in index 4292c2b..37c401f 100644 --- a/Utilities/cmcurl/lib/curl_config-cmake.h.in +++ b/Utilities/cmcurl/lib/curl_config-cmake.h.in
@@ -111,8 +111,8 @@ /* disables netrc parser */ #cmakedefine CURL_DISABLE_NETRC 1 -/* disables NTLM support */ -#cmakedefine CURL_DISABLE_NTLM 1 +/* enables NTLM support */ +#cmakedefine CURL_ENABLE_NTLM 1 /* disables date parsing */ #cmakedefine CURL_DISABLE_PARSEDATE 1 @@ -139,7 +139,7 @@ #cmakedefine CURL_DISABLE_SHUFFLE_DNS 1 /* disables SMB */ -#cmakedefine CURL_DISABLE_SMB 1 +#cmakedefine CURL_ENABLE_SMB 1 /* disables SMTP */ #cmakedefine CURL_DISABLE_SMTP 1 @@ -245,9 +245,6 @@ /* Define to 1 if you have the fseeko declaration. */ #cmakedefine HAVE_DECL_FSEEKO 1 -/* Define to 1 if you have the ftruncate function. */ -#cmakedefine HAVE_FTRUNCATE 1 - /* Define to 1 if you have a working getaddrinfo function. */ #cmakedefine HAVE_GETADDRINFO 1 @@ -438,9 +435,6 @@ /* Define to 1 if you have a working POSIX-style strerror_r function. */ #cmakedefine HAVE_POSIX_STRERROR_R 1 -/* Define to 1 if you have the <pthread.h> header file */ -#cmakedefine HAVE_PTHREAD_H 1 - /* Define to 1 if you have the <pwd.h> header file. */ #cmakedefine HAVE_PWD_H 1 @@ -495,9 +489,6 @@ /* Define to 1 if you have the sigsetjmp function or macro. */ #cmakedefine HAVE_SIGSETJMP 1 -/* Define to 1 if you have the `snprintf' function. */ -#cmakedefine HAVE_SNPRINTF 1 - /* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */ #cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1 @@ -653,14 +644,17 @@ /* Define to 1 if you have the ANSI C header files. */ #cmakedefine STDC_HEADERS 1 +/* Define if you have POSIX pthreads */ +#cmakedefine HAVE_THREADS_POSIX 1 + /* Define if you want to enable c-ares support */ #cmakedefine USE_ARES 1 -/* Define if you want to enable POSIX threaded DNS lookup */ -#cmakedefine USE_THREADS_POSIX 1 +/* Define if you want to enable c-ares DNS lookup */ +#cmakedefine USE_RESOLV_ARES 1 -/* Define if you want to enable Win32 threaded DNS lookup */ -#cmakedefine USE_THREADS_WIN32 1 +/* Define if you want to enable threaded DNS lookup */ +#cmakedefine USE_RESOLV_THREADED 1 /* if GnuTLS is enabled */ #cmakedefine USE_GNUTLS 1 @@ -686,15 +680,15 @@ /* if wolfSSL has the wolfSSL_UseALPN function. */ #cmakedefine HAVE_WOLFSSL_USEALPN 1 -/* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */ -#cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1 - /* if wolfSSL has the wolfSSL_BIO_new function. */ #cmakedefine HAVE_WOLFSSL_BIO_NEW 1 /* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */ #cmakedefine HAVE_WOLFSSL_BIO_SET_SHUTDOWN 1 +/* if wolfSSL has the wc_Des_EcbEncrypt function. */ +#cmakedefine HAVE_WC_DES_ECBENCRYPT 1 + /* if libssh is in use */ #cmakedefine USE_LIBSSH 1 @@ -713,9 +707,6 @@ /* if AmiSSL is in use */ #cmakedefine USE_AMISSL 1 -/* if librtmp/rtmpdump is in use */ -#cmakedefine USE_LIBRTMP 1 - /* if GSASL is in use */ #cmakedefine USE_GSASL 1
diff --git a/Utilities/cmcurl/lib/curl_ctype.h b/Utilities/cmcurl/lib/curl_ctype.h index 153a8f9..f3291ad 100644 --- a/Utilities/cmcurl/lib/curl_ctype.h +++ b/Utilities/cmcurl/lib/curl_ctype.h
@@ -24,8 +24,8 @@ * ***************************************************************************/ -#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f')) -#define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F')) +#define ISLOWHEXALPHA(x) (((x) >= 'a') && ((x) <= 'f')) +#define ISUPHEXALPHA(x) (((x) >= 'A') && ((x) <= 'F')) #define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f) #define IS7F(x) ((x) == 0x7f) @@ -36,7 +36,7 @@ #define ISGRAPH(x) (ISLOWPRINT(x) || (((x) > ' ') && ((x) <= 0x7e))) #define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x)) #define ISALPHA(x) (ISLOWER(x) || ISUPPER(x)) -#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x)) +#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALPHA(x) || ISUPHEXALPHA(x)) #define ISODIGIT(x) (((x) >= '0') && ((x) <= '7')) #define ISALNUM(x) (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x)) #define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z')) @@ -44,9 +44,9 @@ #define ISDIGIT(x) (((x) >= '0') && ((x) <= '9')) #define ISBLANK(x) (((x) == ' ') || ((x) == '\t')) #define ISSPACE(x) (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d))) -#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \ - ((x) == '~')) +#define ISURLPUNTCS(x) \ + (((x) == '-') || ((x) == '.') || ((x) == '_') || ((x) == '~')) #define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x)) -#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') +#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r') #endif /* HEADER_CURL_CTYPE_H */
diff --git a/Utilities/cmcurl/lib/curl_fopen.c b/Utilities/cmcurl/lib/curl_fopen.c index b7a67aa..cc888f7 100644 --- a/Utilities/cmcurl/lib/curl_fopen.c +++ b/Utilities/cmcurl/lib/curl_fopen.c
@@ -103,7 +103,14 @@ return CURLE_OK; } curlx_fclose(*fh); +#ifdef HAVE_GETEUID + /* If the existing file is not owned by the user, do not inherit + * its permissions at the temp file created below. The permissions + * might be unsuitable for holding user private data. */ + if(sb.st_uid != geteuid()) + sb.st_mode = 0; #endif +#endif /* !_WIN32 */ *fh = NULL; result = Curl_rand_alnum(data, randbuf, sizeof(randbuf));
diff --git a/Utilities/cmcurl/lib/curl_get_line.c b/Utilities/cmcurl/lib/curl_get_line.c index 85b3525..6fcd043 100644 --- a/Utilities/cmcurl/lib/curl_get_line.c +++ b/Utilities/cmcurl/lib/curl_get_line.c
@@ -40,6 +40,8 @@ while(1) { size_t rlen; const char *b = fgets(buffer, sizeof(buffer), input); + if(!b && ferror(input)) + return CURLE_READ_ERROR; *eof = feof(input);
diff --git a/Utilities/cmcurl/lib/curl_gssapi.c b/Utilities/cmcurl/lib/curl_gssapi.c index c0eb1c4..650d190 100644 --- a/Utilities/cmcurl/lib/curl_gssapi.c +++ b/Utilities/cmcurl/lib/curl_gssapi.c
@@ -55,7 +55,7 @@ #define CURL_ALIGN8 #endif -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -93,20 +93,20 @@ char creds[250]; }; -static OM_uint32 -stub_gss_init_sec_context(OM_uint32 *min, - gss_cred_id_t initiator_cred_handle, - struct stub_gss_ctx_id_t_desc **context, - gss_name_t target_name, - const gss_OID mech_type, - OM_uint32 req_flags, - OM_uint32 time_req, - const gss_channel_bindings_t input_chan_bindings, - gss_buffer_desc *input_token, - gss_OID *actual_mech_type, - gss_buffer_desc *output_token, - OM_uint32 *ret_flags, - OM_uint32 *time_rec) +static OM_uint32 stub_gss_init_sec_context( + OM_uint32 *min, + gss_cred_id_t initiator_cred_handle, + struct stub_gss_ctx_id_t_desc **context, + gss_name_t target_name, + const gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + const gss_channel_bindings_t input_chan_bindings, + gss_buffer_desc *input_token, + gss_OID *actual_mech_type, + gss_buffer_desc *output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) { struct stub_gss_ctx_id_t_desc *ctx = NULL; @@ -283,10 +283,10 @@ return GSS_S_CONTINUE_NEEDED; } -static OM_uint32 -stub_gss_delete_sec_context(OM_uint32 *min, - struct stub_gss_ctx_id_t_desc **context, - gss_buffer_t output_token) +static OM_uint32 stub_gss_delete_sec_context( + OM_uint32 *min, + struct stub_gss_ctx_id_t_desc **context, + gss_buffer_t output_token) { (void)output_token; @@ -441,7 +441,7 @@ } #endif /* CURLVERBOSE */ -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/curl_gssapi.h b/Utilities/cmcurl/lib/curl_gssapi.h index 0d36097..fc3759e 100644 --- a/Utilities/cmcurl/lib/curl_gssapi.h +++ b/Utilities/cmcurl/lib/curl_gssapi.h
@@ -44,7 +44,7 @@ OM_uint32 *ret_flags); OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, - gss_ctx_id_t *context_handle, + gss_ctx_id_t *context, gss_buffer_t output_token); #ifdef CURLVERBOSE
diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h index b9f218f..301d44f 100644 --- a/Utilities/cmcurl/lib/curl_hmac.h +++ b/Utilities/cmcurl/lib/curl_hmac.h
@@ -57,14 +57,14 @@ struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams, const unsigned char *key, unsigned int keylen); -int Curl_HMAC_update(struct HMAC_context *ctxt, - const unsigned char *data, - unsigned int len); +void Curl_HMAC_update(struct HMAC_context *ctxt, + const unsigned char *data, + unsigned int len); int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output); CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, - const unsigned char *data, const size_t datalen, + const unsigned char *data, size_t datalen, unsigned char *output); #endif
diff --git a/Utilities/cmcurl/lib/curl_ldap.h b/Utilities/cmcurl/lib/curl_ldap.h index 7c1e1c4..0ccc128 100644 --- a/Utilities/cmcurl/lib/curl_ldap.h +++ b/Utilities/cmcurl/lib/curl_ldap.h
@@ -25,9 +25,6 @@ ***************************************************************************/ extern const struct Curl_protocol Curl_protocol_ldap; -extern const struct Curl_scheme Curl_scheme_ldap; -extern const struct Curl_scheme Curl_scheme_ldaps; - void Curl_ldap_version(char *buf, size_t bufsz); #endif /* HEADER_CURL_LDAP_H */
diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h index 042c5f5..8a0cc26 100644 --- a/Utilities/cmcurl/lib/curl_md5.h +++ b/Utilities/cmcurl/lib/curl_md5.h
@@ -54,7 +54,7 @@ extern const struct HMAC_params Curl_HMAC_MD5; CURLcode Curl_md5it(unsigned char *output, const unsigned char *input, - const size_t len); + size_t len); struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params); CURLcode Curl_MD5_update(struct MD5_context *context,
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c index 4a9433b..4b2007b 100644 --- a/Utilities/cmcurl/lib/curl_ntlm_core.c +++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -49,59 +49,30 @@ in NTLM type-3 messages. */ -#ifdef USE_MBEDTLS -#include <mbedtls/version.h> -#if MBEDTLS_VERSION_NUMBER < 0x03020000 -#error "mbedTLS 3.2.0 or later required" -#endif -#endif - #if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT) -# define USE_OPENSSL_DES -#elif defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT) -# define USE_OPENSSL_DES -#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) -# define USE_MBEDTLS_DES -#endif -#ifdef USE_OPENSSL_DES - -#ifdef USE_OPENSSL # include <openssl/des.h> # ifdef OPENSSL_IS_AWSLC /* for versions 1.2.0 to 1.30.1 */ # define DES_set_key_unchecked (void)DES_set_key # endif -# define DESKEY(x) &x -#else +# define USE_OPENSSL_DES + +#elif defined(USE_WOLFSSL) && defined(HAVE_WC_DES_ECBENCRYPT) + # include <wolfssl/options.h> -# include <wolfssl/openssl/des.h> -# include <wolfssl/version.h> -# ifdef OPENSSL_COEXIST -# define DES_key_schedule WOLFSSL_DES_key_schedule -# define DES_cblock WOLFSSL_DES_cblock -# define DES_set_odd_parity wolfSSL_DES_set_odd_parity -# define DES_set_key wolfSSL_DES_set_key -# define DES_set_key_unchecked wolfSSL_DES_set_key_unchecked -# define DES_ecb_encrypt wolfSSL_DES_ecb_encrypt -# define DESKEY(x) ((WOLFSSL_DES_key_schedule *)(x)) -# if defined(LIBWOLFSSL_VERSION_HEX) && LIBWOLFSSL_VERSION_HEX >= 0x05007006 -# define DES_ENCRYPT WC_DES_ENCRYPT -# define DES_DECRYPT WC_DES_DECRYPT -# endif -# else -# define DESKEY(x) &x -# endif -#endif +# include <wolfssl/wolfcrypt/des3.h> +# define USE_WOLFSSL_DES #elif defined(USE_GNUTLS) - # include <nettle/des.h> # define USE_CURL_DES_SET_ODD_PARITY - -#elif defined(USE_MBEDTLS_DES) - +#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB) +# include <mbedtls/version.h> +# if MBEDTLS_VERSION_NUMBER < 0x03020000 +# error "mbedTLS 3.2.0 or later required" +# endif # include <mbedtls/des.h> - +# define USE_MBEDTLS_DES #elif defined(USE_OS400CRYPTO) # include "cipher.mih" /* mih/cipher */ # define USE_CURL_DES_SET_ODD_PARITY @@ -170,7 +141,7 @@ key[4] = (char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4)); key[5] = (char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5)); key[6] = (char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6)); - key[7] = (char) ((key_56[6] << 1) & 0xFF); + key[7] = (char)((key_56[6] << 1) & 0xFF); } #ifdef USE_OPENSSL_DES @@ -192,8 +163,19 @@ DES_set_key_unchecked(&key, ks); } -#elif defined(USE_GNUTLS) +#elif defined(USE_WOLFSSL_DES) +static void setup_des_key(const unsigned char *key_56, Des *des) +{ + byte key[8]; + /* Expand the 56-bit key to 64 bits */ + extend_key_56_to_64(key_56, (char *)key); + + /* Set the key */ + wc_Des_SetKey(des, key, NULL, 0); +} + +#elif defined(USE_GNUTLS) static void setup_des_key(const unsigned char *key_56, struct des_ctx *des) { char key[8]; @@ -209,7 +191,6 @@ } #elif defined(USE_MBEDTLS_DES) - static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -229,7 +210,6 @@ } #elif defined(USE_OS400CRYPTO) - static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -253,7 +233,6 @@ } #elif defined(USE_WIN32_CRYPTO) - static bool encrypt_des(const unsigned char *in, unsigned char *out, const unsigned char *key_56) { @@ -316,17 +295,25 @@ #ifdef USE_OPENSSL_DES DES_key_schedule ks; - setup_des_key(keys, DESKEY(ks)); + setup_des_key(keys, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), - (DES_cblock *)results, DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)results, &ks, DES_ENCRYPT); - setup_des_key(keys + 7, DESKEY(ks)); + setup_des_key(keys + 7, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), - (DES_cblock *)(results + 8), DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)(results + 8), &ks, DES_ENCRYPT); - setup_des_key(keys + 14, DESKEY(ks)); + setup_des_key(keys + 14, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(plaintext), - (DES_cblock *)(results + 16), DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)(results + 16), &ks, DES_ENCRYPT); +#elif defined(USE_WOLFSSL_DES) + Des des; + setup_des_key(keys, &des); + wc_Des_EcbEncrypt(&des, results, plaintext, DES_KEY_SIZE); + setup_des_key(keys + 7, &des); + wc_Des_EcbEncrypt(&des, results + 8, plaintext, DES_KEY_SIZE); + setup_des_key(keys + 14, &des); + wc_Des_EcbEncrypt(&des, results + 16, plaintext, DES_KEY_SIZE); #elif defined(USE_GNUTLS) struct des_ctx des; setup_des_key(keys, &des); @@ -364,17 +351,22 @@ { /* Create LanManager hashed password. */ - #ifdef USE_OPENSSL_DES DES_key_schedule ks; - setup_des_key(pw, DESKEY(ks)); + setup_des_key(pw, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic), - (DES_cblock *)lmbuffer, DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)lmbuffer, &ks, DES_ENCRYPT); - setup_des_key(pw + 7, DESKEY(ks)); + setup_des_key(pw + 7, &ks); DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic), - (DES_cblock *)(lmbuffer + 8), DESKEY(ks), DES_ENCRYPT); + (DES_cblock *)(lmbuffer + 8), &ks, DES_ENCRYPT); +#elif defined(USE_WOLFSSL_DES) + Des des; + setup_des_key(pw, &des); + wc_Des_EcbEncrypt(&des, lmbuffer, magic, DES_KEY_SIZE); + setup_des_key(pw + 7, &des); + wc_Des_EcbEncrypt(&des, lmbuffer + 8, magic, DES_KEY_SIZE); #elif defined(USE_GNUTLS) struct des_ctx des; setup_des_key(pw, &des);
diff --git a/Utilities/cmcurl/lib/curl_rtmp.c b/Utilities/cmcurl/lib/curl_rtmp.c deleted file mode 100644 index 75b8091..0000000 --- a/Utilities/cmcurl/lib/curl_rtmp.c +++ /dev/null
@@ -1,326 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) Howard Chu, <hyc@highlandsun.com> - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "curl_setup.h" - -#include "curl_rtmp.h" -#include "urldata.h" - -#ifdef USE_LIBRTMP - -#include "url.h" -#include "curlx/nonblock.h" -#include "progress.h" /* for Curl_pgrsSetUploadSize */ -#include "transfer.h" -#include "bufref.h" - -#include <librtmp/rtmp.h> - -#if defined(USE_WINSOCK) || defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD) -#define SET_RCVTIMEO(tv, s) int tv = s * 1000 -#else -#define SET_RCVTIMEO(tv, s) struct timeval tv = { s, 0 } -#endif - -#define DEF_BUFTIME (2 * 60 * 60 * 1000) /* 2 hours */ - -/* meta key for storing RTMP* at connection */ -#define CURL_META_RTMP_CONN "meta:proto:rtmp:conn" - -static Curl_recv rtmp_recv; -static Curl_send rtmp_send; - -static void rtmp_conn_dtor(void *key, size_t klen, void *entry) -{ - RTMP *r = entry; - (void)key; - (void)klen; - RTMP_Close(r); - RTMP_Free(r); -} - -static CURLcode rtmp_setup_connection(struct Curl_easy *data, - struct connectdata *conn) -{ - RTMP *r = RTMP_Alloc(); - if(!r || - Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor)) - return CURLE_OUT_OF_MEMORY; - - RTMP_Init(r); - RTMP_SetBufferMS(r, DEF_BUFTIME); - if(!RTMP_SetupURL(r, CURL_UNCONST(Curl_bufref_ptr(&data->state.url)))) - /* rtmp_conn_dtor() performs the cleanup */ - return CURLE_URL_MALFORMAT; - return CURLE_OK; -} - -static CURLcode rtmp_connect(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - SET_RCVTIMEO(tv, 10); - - if(!r) - return CURLE_FAILED_INIT; - - if(conn->sock[FIRSTSOCKET] > INT_MAX) { - /* The socket value is invalid for rtmp. */ - return CURLE_FAILED_INIT; - } - - r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET]; - - /* We have to know if it is a write before we send the - * connect request packet - */ - if(data->state.upload) - r->Link.protocol |= RTMP_FEATURE_WRITE; - - /* For plain streams, use the buffer toggle trick to keep data flowing */ - if(!(r->Link.lFlags & RTMP_LF_LIVE) && - !(r->Link.protocol & RTMP_FEATURE_HTTP)) - r->Link.lFlags |= RTMP_LF_BUFX; - - (void)curlx_nonblock(r->m_sb.sb_socket, FALSE); - setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, - (char *)&tv, sizeof(tv)); - - if(!RTMP_Connect1(r, NULL)) - return CURLE_FAILED_INIT; - - /* Clients must send a periodic BytesReceived report to the server */ - r->m_bSendCounter = TRUE; - - *done = TRUE; - conn->recv[FIRSTSOCKET] = rtmp_recv; - conn->send[FIRSTSOCKET] = rtmp_send; - return CURLE_OK; -} - -static CURLcode rtmp_do(struct Curl_easy *data, bool *done) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - - if(!r || !RTMP_ConnectStream(r, 0)) - return CURLE_FAILED_INIT; - - if(data->state.upload) { - Curl_pgrsSetUploadSize(data, data->state.infilesize); - Curl_xfer_setup_send(data, FIRSTSOCKET); - } - else - Curl_xfer_setup_recv(data, FIRSTSOCKET, -1); - *done = TRUE; - return CURLE_OK; -} - -static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status, - bool premature) -{ - (void)data; - (void)status; - (void)premature; - - return CURLE_OK; -} - -static CURLcode rtmp_disconnect(struct Curl_easy *data, - struct connectdata *conn, - bool dead_connection) -{ - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - (void)data; - (void)dead_connection; - if(r) - Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN); - return CURLE_OK; -} - -static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, size_t *pnread) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - CURLcode result = CURLE_OK; - ssize_t rv; - - (void)sockindex; - *pnread = 0; - if(!r) - return CURLE_FAILED_INIT; - - rv = RTMP_Read(r, buf, curlx_uztosi(len)); - if(!curlx_sztouz(rv, pnread)) { - if(r->m_read.status == RTMP_READ_COMPLETE || - r->m_read.status == RTMP_READ_EOF) { - data->req.size = data->req.bytecount; - } - else - result = CURLE_RECV_ERROR; - } - - return result; -} - -static CURLcode rtmp_send(struct Curl_easy *data, int sockindex, - const uint8_t *buf, size_t len, bool eos, - size_t *pnwritten) -{ - struct connectdata *conn = data->conn; - RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - ssize_t rv; - - (void)sockindex; - (void)eos; - *pnwritten = 0; - if(!r) - return CURLE_FAILED_INIT; - - rv = RTMP_Write(r, (const char *)buf, curlx_uztosi(len)); - if(!curlx_sztouz(rv, pnwritten)) - return CURLE_SEND_ERROR; - - return CURLE_OK; -} - -void Curl_rtmp_version(char *version, size_t len) -{ - char suff[2]; - if(RTMP_LIB_VERSION & 0xff) { - suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1; - suff[1] = '\0'; - } - else - suff[0] = '\0'; - - curl_msnprintf(version, len, "librtmp/%d.%d%s", - RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff, - suff); -} - -/* - * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu/ - */ - -static const struct Curl_protocol Curl_protocol_rtmp = { - rtmp_setup_connection, /* setup_connection */ - rtmp_do, /* do_it */ - rtmp_done, /* done */ - ZERO_NULL, /* do_more */ - rtmp_connect, /* connect_it */ - ZERO_NULL, /* connecting */ - ZERO_NULL, /* doing */ - ZERO_NULL, /* proto_pollset */ - ZERO_NULL, /* doing_pollset */ - ZERO_NULL, /* domore_pollset */ - ZERO_NULL, /* perform_pollset */ - rtmp_disconnect, /* disconnect */ - ZERO_NULL, /* write_resp */ - ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ - ZERO_NULL, /* attach connection */ - ZERO_NULL, /* follow */ -}; - -#endif /* USE_LIBRTMP */ - -const struct Curl_scheme Curl_scheme_rtmp = { - "rtmp", /* scheme */ -#ifndef USE_LIBRTMP - ZERO_NULL, -#else - &Curl_protocol_rtmp, -#endif - CURLPROTO_RTMP, /* protocol */ - CURLPROTO_RTMP, /* family */ - PROTOPT_NONE, /* flags */ - PORT_RTMP, /* defport */ -}; - -const struct Curl_scheme Curl_scheme_rtmpt = { - "rtmpt", /* scheme */ -#ifndef USE_LIBRTMP - ZERO_NULL, -#else - &Curl_protocol_rtmp, -#endif - CURLPROTO_RTMPT, /* protocol */ - CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE, /* flags */ - PORT_RTMPT, /* defport */ -}; - -const struct Curl_scheme Curl_scheme_rtmpe = { - "rtmpe", /* scheme */ -#ifndef USE_LIBRTMP - ZERO_NULL, -#else - &Curl_protocol_rtmp, -#endif - CURLPROTO_RTMPE, /* protocol */ - CURLPROTO_RTMPE, /* family */ - PROTOPT_NONE, /* flags */ - PORT_RTMP, /* defport */ -}; - -const struct Curl_scheme Curl_scheme_rtmpte = { - "rtmpte", /* scheme */ -#ifndef USE_LIBRTMP - ZERO_NULL, -#else - &Curl_protocol_rtmp, -#endif - CURLPROTO_RTMPTE, /* protocol */ - CURLPROTO_RTMPTE, /* family */ - PROTOPT_NONE, /* flags */ - PORT_RTMPT, /* defport */ -}; - -const struct Curl_scheme Curl_scheme_rtmps = { - "rtmps", /* scheme */ -#ifndef USE_LIBRTMP - ZERO_NULL, -#else - &Curl_protocol_rtmp, -#endif - CURLPROTO_RTMPS, /* protocol */ - CURLPROTO_RTMP, /* family */ - PROTOPT_NONE, /* flags */ - PORT_RTMPS, /* defport */ -}; - -const struct Curl_scheme Curl_scheme_rtmpts = { - "rtmpts", /* scheme */ -#ifndef USE_LIBRTMP - ZERO_NULL, -#else - &Curl_protocol_rtmp, -#endif - CURLPROTO_RTMPTS, /* protocol */ - CURLPROTO_RTMPT, /* family */ - PROTOPT_NONE, /* flags */ - PORT_RTMPS, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/curl_rtmp.h b/Utilities/cmcurl/lib/curl_rtmp.h deleted file mode 100644 index e57d20c..0000000 --- a/Utilities/cmcurl/lib/curl_rtmp.h +++ /dev/null
@@ -1,36 +0,0 @@ -#ifndef HEADER_CURL_RTMP_H -#define HEADER_CURL_RTMP_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Howard Chu, <hyc@highlandsun.com> - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_rtmp; -extern const struct Curl_scheme Curl_scheme_rtmpt; -extern const struct Curl_scheme Curl_scheme_rtmpe; -extern const struct Curl_scheme Curl_scheme_rtmpte; -extern const struct Curl_scheme Curl_scheme_rtmps; -extern const struct Curl_scheme Curl_scheme_rtmpts; -#ifdef USE_LIBRTMP -void Curl_rtmp_version(char *version, size_t len); -#endif - -#endif /* HEADER_CURL_RTMP_H */
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c index b2eb1fe..60f0859 100644 --- a/Utilities/cmcurl/lib/curl_sasl.c +++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -415,9 +415,8 @@ data->set.str[STRING_SERVICE_NAME] : sctx->sasl->params->service; const char *hostname; - int port; - Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); + Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, NULL); sctx->mech = SASL_MECH_STRING_NTLM; sctx->state1 = SASL_NTLM;
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h index 90d2ff7..2494152 100644 --- a/Utilities/cmcurl/lib/curl_setup.h +++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -224,7 +224,7 @@ /* ================================================================ */ /* Give calloc a chance to be dragging in early, so we do not redefine */ -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) +#ifdef HAVE_THREADS_POSIX # include <pthread.h> #endif @@ -262,9 +262,6 @@ # ifndef CURL_DISABLE_RTSP # define CURL_DISABLE_RTSP # endif -# ifndef CURL_DISABLE_SMB -# define CURL_DISABLE_SMB -# endif # ifndef CURL_DISABLE_SMTP # define CURL_DISABLE_SMTP # endif @@ -372,16 +369,14 @@ #endif /* based on logic in "curl/mprintf.h" */ -#if (defined(__GNUC__) || defined(__clang__) || \ - defined(__IAR_SYSTEMS_ICC__)) && \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ +#if (defined(__GNUC__) || defined(__clang__) || \ + defined(__IAR_SYSTEMS_ICC__)) && \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ !defined(CURL_NO_FMT_CHECKS) #if defined(__MINGW32__) && !defined(__clang__) -#define CURL_PRINTF(fmt, arg) \ - __attribute__((format(gnu_printf, fmt, arg))) +#define CURL_PRINTF(fmt, arg) __attribute__((format(gnu_printf, fmt, arg))) #else -#define CURL_PRINTF(fmt, arg) \ - __attribute__((format(__printf__, fmt, arg))) +#define CURL_PRINTF(fmt, arg) __attribute__((format(__printf__, fmt, arg))) #endif #else #define CURL_PRINTF(fmt, arg) @@ -399,7 +394,7 @@ (defined(__GNUC__) && __GNUC__ <= 14)) && \ defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ !defined(__ENVIRONMENT_OS_VERSION_MIN_REQUIRED__) -#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \ +#define __ENVIRONMENT_OS_VERSION_MIN_REQUIRED__ \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ #endif @@ -446,7 +441,7 @@ # if !(defined(__NEWLIB__) || \ (defined(__CLIB2__) && defined(__THREAD_SAFE))) /* disable threaded resolver with clib2 - requires newlib or clib-ts */ -# undef USE_THREADS_POSIX +# undef USE_RESOLV_THREADED # endif # endif # include <exec/types.h> @@ -700,6 +695,16 @@ #endif /* _WIN32 */ +/* We want to use mutex when available. */ +#if defined(HAVE_THREADS_POSIX) || defined(_WIN32) +#define USE_MUTEX +#endif + +/* threaded resolver is the only feature requiring threads. */ +#ifdef USE_RESOLV_THREADED +#define USE_THREADS +#endif + /* ---------------------------------------------------------------- */ /* resolver specialty compile-time defines */ /* CURLRES_* defines to use in the host*.c sources */ @@ -717,12 +722,10 @@ # define CURLRES_IPV4 #endif -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_RESOLV_THREADED # define CURLRES_ASYNCH -# define CURLRES_THREADED -#elif defined(USE_ARES) +#elif defined(USE_RESOLV_ARES) # define CURLRES_ASYNCH -# define CURLRES_ARES /* now undef the stock libc functions to avoid them being used */ # undef HAVE_GETADDRINFO # undef HAVE_FREEADDRINFO @@ -743,8 +746,7 @@ #endif #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ - defined(USE_RUSTLS) + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif @@ -760,24 +762,24 @@ #endif /* Single point where USE_SPNEGO definition might be defined */ -#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ - (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \ + (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_SPNEGO #endif /* Single point where USE_KERBEROS5 definition might be defined */ -#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \ - (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) +#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \ + (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) #define USE_KERBEROS5 #endif /* Single point where USE_NTLM definition might be defined */ -#ifndef CURL_DISABLE_NTLM -# if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) || \ - defined(USE_GNUTLS) || \ - (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) || \ - defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ - (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) +#ifdef CURL_ENABLE_NTLM +# if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) || \ + defined(USE_GNUTLS) || \ + (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) || \ + defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ + (defined(USE_WOLFSSL) && defined(HAVE_WC_DES_ECBENCRYPT)) # define USE_CURL_NTLM_CORE # endif # if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI) @@ -789,6 +791,16 @@ #define USE_SSH #endif +/* GCC <4.6 does not support '#pragma GCC diagnostic push' and does not support + 'pragma GCC diagnostic' inside functions. + Use CURL_HAVE_DIAG to guard the above in the curl codebase, instead of + defined(__GNUC__) || defined(__clang__). + */ +#if defined(__clang__) || (defined(__GNUC__) && \ + ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)))) +#define CURL_HAVE_DIAG +#endif + /* * Provide a mechanism to silence picky compilers, such as gcc 4.6+. * Parameters should of course normally not be unused, but for example when @@ -819,8 +831,8 @@ /* fallthrough attribute */ #ifndef FALLTHROUGH -#if (defined(__GNUC__) && __GNUC__ >= 7) || \ - (defined(__clang__) && __clang_major__ >= 10) +#if (defined(__GNUC__) && __GNUC__ >= 7) || \ + (defined(__clang__) && __clang_major__ >= 10) # define FALLTHROUGH() __attribute__((fallthrough)) #else # define FALLTHROUGH() do {} while(0) @@ -1015,7 +1027,7 @@ * 'bool' stuff compatible with HP-UX headers. */ #if defined(__hpux) && !defined(HAVE_BOOL_T) - typedef int bool; +typedef int bool; # define false 0 # define true 1 # define HAVE_BOOL_T @@ -1028,10 +1040,10 @@ * global namespace though, so use bool_false and bool_true. */ #ifndef HAVE_BOOL_T - typedef enum { - bool_false = 0, - bool_true = 1 - } bool; +typedef enum { + bool_false = 0, + bool_true = 1 +} bool; /* * Use a define to let 'true' and 'false' use those enums. There @@ -1322,14 +1334,14 @@ extern curl_calloc_callback Curl_ccalloc; /* - * Curl_safefree defined as a macro to allow MemoryTracking feature - * to log free() calls at same location where Curl_safefree is used. + * curlx_safefree() defined as a macro to allow MemoryTracking feature + * to log free() calls at same location where curlx_safefree() is used. * This macro also assigns NULL to given pointer when free'd. */ -#define Curl_safefree(ptr) \ - do { \ - curlx_free(ptr); \ - (ptr) = NULL; \ +#define curlx_safefree(ptr) \ + do { \ + curlx_free(ptr); \ + (ptr) = NULL; \ } while(0) #include <curl/curl.h> /* for CURL_EXTERN, curl_socket_t, mprintf.h */ @@ -1390,7 +1402,8 @@ int line, const char *source); CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source); -CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen, +CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, + void *saddr, void *saddrlen, int line, const char *source); #ifdef HAVE_ACCEPT4 CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, @@ -1522,9 +1535,7 @@ #define USE_HTTP2 #endif -#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ - (defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \ - defined(USE_QUICHE) +#if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE) #ifdef CURL_WITH_MULTI_SSL #error "MultiSSL combined with QUIC is not supported"
diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h index bc1512e..6c48a8e 100644 --- a/Utilities/cmcurl/lib/curl_sha256.h +++ b/Utilities/cmcurl/lib/curl_sha256.h
@@ -38,7 +38,7 @@ #endif CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, - const size_t len); + size_t len); #endif
diff --git a/Utilities/cmcurl/lib/curl_sha512_256.c b/Utilities/cmcurl/lib/curl_sha512_256.c index 648e3e3..73b959c 100644 --- a/Utilities/cmcurl/lib/curl_sha512_256.c +++ b/Utilities/cmcurl/lib/curl_sha512_256.c
@@ -28,12 +28,12 @@ #include "curl_sha512_256.h" /* The recommended order of the TLS backends: - * 1. OpenSSL - * 2. GnuTLS - * 3. wolfSSL - * 4. Schannel SSPI - * 5. mbedTLS - * 6. Rustls + * 1. USE_OPENSSL + * 2. USE_WOLFSSL + * 3. USE_GNUTLS + * 4. USE_MBEDTLS (TBD) + * 5. USE_RUSTLS (TBD) + * 6. USE_WIN32_CRYPTO (TBD) * Skip the backend if it does not support the required algorithm */ #ifdef USE_OPENSSL @@ -66,6 +66,14 @@ # endif #endif /* USE_OPENSSL */ +#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_WOLFSSL) +# include <wolfssl/options.h> +# ifndef WOLFSSL_NOSHA512_256 +# define USE_WOLFSSL_SHA512_256 1 +# define HAS_SHA512_256_IMPLEMENTATION 1 +# endif +#endif + #if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS) # include <nettle/sha.h> # ifdef SHA512_256_DIGEST_SIZE @@ -153,26 +161,63 @@ */ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context) { - CURLcode ret; + CURLcode result; Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context; #ifdef NEED_NETBSD_SHA512_256_WORKAROUND /* Use a larger buffer to work around a bug in NetBSD: https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */ unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2]; - ret = EVP_DigestFinal_ex(*ctx, + result = EVP_DigestFinal_ex(*ctx, tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; - if(ret == CURLE_OK) + if(result == CURLE_OK) memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE); explicit_memset(tmp_digest, 0, sizeof(tmp_digest)); #else /* !NEED_NETBSD_SHA512_256_WORKAROUND */ - ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER; + result = EVP_DigestFinal_ex(*ctx, digest, NULL) ? + CURLE_OK : CURLE_SSL_CIPHER; #endif /* NEED_NETBSD_SHA512_256_WORKAROUND */ EVP_MD_CTX_destroy(*ctx); *ctx = NULL; - return ret; + return result; +} + +#elif defined(USE_WOLFSSL_SHA512_256) +#include <wolfssl/wolfcrypt/sha512.h> + +#define CURL_SHA512_256_DIGEST_SIZE WC_SHA512_256_DIGEST_SIZE +#define CURL_SHA512_256_BLOCK_SIZE WC_SHA512_256_BLOCK_SIZE + +typedef struct wc_Sha512 Curl_sha512_256_ctx; + +static CURLcode Curl_sha512_256_init(void *ctx) +{ + if(wc_InitSha512_256(ctx)) + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static CURLcode Curl_sha512_256_update(void *ctx, + const unsigned char *data, + size_t length) +{ + do { + word32 ilen = (word32)CURLMIN(length, UINT_MAX); + if(wc_Sha512_256Update(ctx, data, ilen)) + return CURLE_SSL_CIPHER; + length -= ilen; + data += ilen; + } while(length); + return CURLE_OK; +} + +static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx) +{ + if(wc_Sha512_256Final(ctx, digest)) + return CURLE_SSL_CIPHER; + return CURLE_OK; } #elif defined(USE_GNUTLS_SHA512_256) @@ -247,10 +292,7 @@ /* ** This implementation of SHA-512/256 hash calculation was originally ** * * ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd. ** * * ** The author ported the code to libcurl. The ported code is provided ** * - * ** under curl license. ** * - * ** This is a minimal version with minimal optimizations. Performance ** * - * ** can be significantly improved. Big-endian store and load macros ** * - * ** are obvious targets for optimization. ** */ + * ** under curl license. ** */ #ifdef __GNUC__ # if defined(__has_attribute) && defined(__STDC_VERSION__) @@ -751,17 +793,17 @@ size_t input_size) { Curl_sha512_256_ctx ctx; - CURLcode res; + CURLcode result; - res = Curl_sha512_256_init(&ctx); - if(res != CURLE_OK) - return res; + result = Curl_sha512_256_init(&ctx); + if(result != CURLE_OK) + return result; - res = Curl_sha512_256_update(&ctx, (const void *)input, input_size); + result = Curl_sha512_256_update(&ctx, (const void *)input, input_size); - if(res != CURLE_OK) { + if(result != CURLE_OK) { (void)Curl_sha512_256_finish(output, &ctx); - return res; + return result; } return Curl_sha512_256_finish(output, &ctx);
diff --git a/Utilities/cmcurl/lib/curl_share.c b/Utilities/cmcurl/lib/curl_share.c index 338727a..386a2b5 100644 --- a/Utilities/cmcurl/lib/curl_share.c +++ b/Utilities/cmcurl/lib/curl_share.c
@@ -24,22 +24,61 @@ #include "curl_setup.h" #include "urldata.h" +#include "multiif.h" +#include "curl_threads.h" #include "curl_share.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" #include "hsts.h" #include "url.h" +static void share_destroy(struct Curl_share *share) +{ + if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { + Curl_cpool_destroy(&share->cpool); + } + + Curl_dnscache_destroy(&share->dnscache); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + Curl_cookie_cleanup(share->cookies); +#endif + +#ifndef CURL_DISABLE_HSTS + Curl_hsts_cleanup(&share->hsts); +#endif + +#ifdef USE_SSL + if(share->ssl_scache) { + Curl_ssl_scache_destroy(share->ssl_scache); + share->ssl_scache = NULL; + } +#endif + + Curl_psl_destroy(&share->psl); + Curl_close(&share->admin); + +#ifdef USE_MUTEX + Curl_mutex_destroy(&share->lock); +#endif + share->magic = 0; + curlx_free(share); +} + CURLSH *curl_share_init(void) { struct Curl_share *share = curlx_calloc(1, sizeof(struct Curl_share)); if(share) { share->magic = CURL_GOOD_SHARE; share->specifier |= (1 << CURL_LOCK_DATA_SHARE); +#ifdef USE_MUTEX + Curl_mutex_init(&share->lock); +#endif + share->ref_count = 1; Curl_dnscache_init(&share->dnscache, 23); share->admin = curl_easy_init(); if(!share->admin) { - curlx_free(share); + share_destroy(share); return NULL; } /* admin handles have mid 0 */ @@ -50,10 +89,104 @@ share->admin->set.verbose = TRUE; #endif } - return share; } +static uint32_t share_ref_inc(struct Curl_share *share) +{ + uint32_t n; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + n = ++(share->ref_count); + share->has_been_shared = TRUE; + Curl_mutex_release(&share->lock); +#else + n = ++(share->ref_count); + share->has_been_shared = TRUE; +#endif + return n; +} + +static uint32_t share_ref_dec(struct Curl_share *share) +{ + uint32_t n; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + DEBUGASSERT(share->ref_count); + n = --(share->ref_count); + Curl_mutex_release(&share->lock); +#else + n = --(share->ref_count); +#endif + return n; +} + +static bool share_has_been_shared(struct Curl_share *share) +{ + bool was_shared; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + was_shared = share->has_been_shared; + Curl_mutex_release(&share->lock); +#else + was_shared = share->has_been_shared; +#endif + return was_shared; +} + +static bool share_lock_acquire(struct Curl_share *share, + struct Curl_easy *data) +{ + if(share->lockfunc && share->unlockfunc && + (data || share_has_been_shared(share))) { + share->lockfunc(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE, + share->clientdata); + return TRUE; + } + return FALSE; +} + +static void share_lock_release(struct Curl_share *share, + struct Curl_easy *data, + bool locked) +{ + if(locked) { + DEBUGASSERT(share->unlockfunc); + if(share->unlockfunc) + share->unlockfunc(data, CURL_LOCK_DATA_SHARE, share->clientdata); + } +} + +static bool share_in_use(struct Curl_share *share) +{ + bool in_use; +#ifdef USE_MUTEX + Curl_mutex_acquire(&share->lock); + in_use = (share->ref_count > 1); + Curl_mutex_release(&share->lock); +#else + bool locked = share_lock_acquire(share, NULL); + in_use = (share->ref_count > 1); + share_lock_release(share, NULL, locked); +#endif + return in_use; +} + +static void share_unlink(struct Curl_share **pshare, + struct Curl_easy *data, + bool locked) +{ + struct Curl_share *share = *pshare; + uint32_t n; + + *pshare = NULL; + n = share_ref_dec(share); + if(locked) + share_lock_release(share, data, locked); + if(!n) /* last reference gone */ + share_destroy(share); +} + #undef curl_share_setopt CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...) { @@ -68,10 +201,11 @@ if(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; - if(share->dirty) + if(share_in_use(share)) { /* do not allow setting options while one or more handles are already using this share */ return CURLSHE_IN_USE; + } va_start(param, option); @@ -114,8 +248,8 @@ /* There is no way (yet) for the application to configure the * session cache size, shared between many transfers. As for curl * itself, a high session count will impact startup time. Also, the - * scache is not optimized for several hundreds of peers. So, - * keep it at a reasonable level. */ + * scache is not optimized for several hundreds of peers. + * Keep it at a reasonable level. */ if(Curl_ssl_scache_create(25, 2, &share->ssl_scache)) res = CURLSHE_NOMEM; } @@ -147,7 +281,6 @@ case CURLSHOPT_UNSHARE: /* this is a type this share will no longer share */ type = va_arg(param, int); - share->specifier &= ~(unsigned int)(1 << type); switch(type) { case CURL_LOCK_DATA_DNS: break; @@ -191,6 +324,8 @@ res = CURLSHE_BAD_OPTION; break; } + if(!res) + share->specifier &= ~(unsigned int)(1 << type); break; case CURLSHOPT_LOCKFUNC: @@ -221,48 +356,15 @@ CURLSHcode curl_share_cleanup(CURLSH *sh) { struct Curl_share *share = sh; + bool locked; if(!GOOD_SHARE_HANDLE(share)) return CURLSHE_INVALID; - if(share->lockfunc) - share->lockfunc(NULL, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE, - share->clientdata); - - if(share->dirty) { - if(share->unlockfunc) - share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); + if(share_in_use(share)) return CURLSHE_IN_USE; - } - if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) { - Curl_cpool_destroy(&share->cpool); - } - - Curl_dnscache_destroy(&share->dnscache); - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - Curl_cookie_cleanup(share->cookies); -#endif - -#ifndef CURL_DISABLE_HSTS - Curl_hsts_cleanup(&share->hsts); -#endif - -#ifdef USE_SSL - if(share->ssl_scache) { - Curl_ssl_scache_destroy(share->ssl_scache); - share->ssl_scache = NULL; - } -#endif - - Curl_psl_destroy(&share->psl); - Curl_close(&share->admin); - - if(share->unlockfunc) - share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata); - share->magic = 0; - curlx_free(share); - + locked = share_lock_acquire(share, NULL); + share_unlink(&share, NULL, locked); return CURLSHE_OK; } @@ -297,3 +399,74 @@ return CURLSHE_OK; } + +CURLcode Curl_share_easy_unlink(struct Curl_easy *data) +{ + struct Curl_share *share = data->share; + + if(share) { + bool locked = share_lock_acquire(share, data); + + /* If data has a connection from this share, detach it. */ + if(data->conn && (share->specifier & (1 << CURL_LOCK_DATA_CONNECT))) + Curl_detach_connection(data); + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(share->cookies == data->cookies) + data->cookies = NULL; +#endif + +#ifndef CURL_DISABLE_HSTS + if(share->hsts == data->hsts) + data->hsts = NULL; +#endif +#ifdef USE_LIBPSL + if(&share->psl == data->psl) + data->psl = data->multi ? &data->multi->psl : NULL; +#endif + + share_unlink(&data->share, data, locked); + } + return CURLE_OK; +} + +CURLcode Curl_share_easy_link(struct Curl_easy *data, + struct Curl_share *share) +{ + if(data->share) { + DEBUGASSERT(0); + return CURLE_FAILED_INIT; + } + + if(share) { + bool locked = share_lock_acquire(share, data); + + share_ref_inc(share); + data->share = share; + +#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) + if(share->cookies) { + /* use shared cookie list, first free own one if any */ + Curl_cookie_cleanup(data->cookies); + /* enable cookies since we now use a share that uses cookies! */ + data->cookies = share->cookies; + } +#endif /* CURL_DISABLE_HTTP */ +#ifndef CURL_DISABLE_HSTS + if(share->hsts) { + /* first free the private one if any */ + Curl_hsts_cleanup(&data->hsts); + data->hsts = share->hsts; + } +#endif +#ifdef USE_LIBPSL + if(share->specifier & (1 << CURL_LOCK_DATA_PSL)) + data->psl = &share->psl; +#endif + + /* check for host cache not needed, + * it will be done by curl_easy_perform */ + share_lock_release(share, data, locked); + } + return CURLE_OK; +}
diff --git a/Utilities/cmcurl/lib/curl_share.h b/Utilities/cmcurl/lib/curl_share.h index 5e3f82a..69001be 100644 --- a/Utilities/cmcurl/lib/curl_share.h +++ b/Utilities/cmcurl/lib/curl_share.h
@@ -25,6 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" +#include "curl_threads.h" #include "cookie.h" #include "psl.h" #include "urldata.h" @@ -43,12 +44,22 @@ struct Curl_share { unsigned int magic; /* CURL_GOOD_SHARE */ unsigned int specifier; - volatile unsigned int dirty; + uint32_t ref_count; +#ifdef USE_MUTEX + /* do `ref_count` and `has_been_shared` checks using this mutex. */ + curl_mutex_t lock; + int has_been_shared; +#else + /* this only ever goes from FALSE -> TRUE once. We need to check + * this without being able to use the `lockfunc`. */ + volatile int has_been_shared; +#endif curl_lock_function lockfunc; curl_unlock_function unlockfunc; void *clientdata; struct Curl_easy *admin; + struct cpool cpool; struct Curl_dnscache dnscache; /* DNS cache */ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) @@ -74,4 +85,8 @@ ((data)->share->specifier & \ (1 << CURL_LOCK_DATA_SSL_SESSION))) +CURLcode Curl_share_easy_unlink(struct Curl_easy *data); +CURLcode Curl_share_easy_link(struct Curl_easy *data, + struct Curl_share *share); + #endif /* HEADER_CURL_SHARE_H */
diff --git a/Utilities/cmcurl/lib/curl_sspi.c b/Utilities/cmcurl/lib/curl_sspi.c index 1d4cf92..3ea1762 100644 --- a/Utilities/cmcurl/lib/curl_sspi.c +++ b/Utilities/cmcurl/lib/curl_sspi.c
@@ -198,9 +198,9 @@ void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity) { if(identity) { - Curl_safefree(identity->User); - Curl_safefree(identity->Password); - Curl_safefree(identity->Domain); + curlx_safefree(identity->User); + curlx_safefree(identity->Password); + curlx_safefree(identity->Domain); } }
diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c index f5fea98..01041f6 100644 --- a/Utilities/cmcurl/lib/curl_threads.c +++ b/Utilities/cmcurl/lib/curl_threads.c
@@ -22,14 +22,12 @@ * ***************************************************************************/ #include "curl_setup.h" - -#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) -#include <pthread.h> -#endif - #include "curl_threads.h" +#include "curlx/timeval.h" -#ifdef USE_THREADS_POSIX +#ifdef USE_THREADS + +#ifdef HAVE_THREADS_POSIX struct Curl_actual_call { unsigned int (*func)(void *); @@ -49,8 +47,8 @@ return 0; } -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg) +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg) { curl_thread_t t = curlx_malloc(sizeof(pthread_t)); struct Curl_actual_call *ac = NULL; @@ -97,10 +95,10 @@ return ret; } -#elif defined(USE_THREADS_WIN32) +#elif defined(_WIN32) -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg) +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg) { curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL); if(!t) { @@ -131,4 +129,86 @@ return ret; } -#endif /* USE_THREADS_* */ +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined +#endif +#endif /* USE_THREADS */ + +#ifdef USE_MUTEX + +#ifdef HAVE_THREADS_POSIX + +void Curl_cond_signal(pthread_cond_t *c) +{ + /* return code defined as always 0 */ + (void)pthread_cond_signal(c); +} + +void Curl_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) +{ + /* return code defined as always 0 */ + (void)pthread_cond_wait(c, m); +} + +CURLcode Curl_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, + uint32_t timeout_ms) +{ + struct curltime now; + struct timespec ts; + timediff_t usec; + int rc; + + /* POSIX expects an "absolute" time until the condition wait ends. + * We cannot use `curlx_now()` here that may run on some monotonic clock + * that will be most likely in the past, as far as POSIX abstime is + * concerned. */ +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; + (void)gettimeofday(&tv, NULL); + now.tv_sec = tv.tv_sec; + now.tv_usec = (int)tv.tv_usec; +#else + now.tv_sec = time(NULL); + now.tv_usec = 0; +#endif + + ts.tv_sec = now.tv_sec + (timeout_ms / 1000); + usec = now.tv_usec + ((timeout_ms % 1000) * 1000); + if(usec >= 1000000) { + ++ts.tv_sec; + usec %= 1000000; + } + ts.tv_nsec = (long)usec * 1000; + + rc = pthread_cond_timedwait(c, m, &ts); + if(rc == SOCKETIMEDOUT) + return CURLE_OPERATION_TIMEDOUT; + return rc ? CURLE_UNRECOVERABLE_POLL : CURLE_OK; +} + +#elif defined(_WIN32) + +void Curl_cond_signal(CONDITION_VARIABLE *c) +{ + WakeConditionVariable(c); +} + +void Curl_cond_wait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m) +{ + SleepConditionVariableCS(c, m, INFINITE); +} + +CURLcode Curl_cond_timedwait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m, + uint32_t timeout_ms) +{ + if(!SleepConditionVariableCS(c, m, (DWORD)timeout_ms)) { + DWORD err = GetLastError(); + return (err == ERROR_TIMEOUT) ? + CURLE_OPERATION_TIMEDOUT : CURLE_UNRECOVERABLE_POLL; + } + return CURLE_OK; +} +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined +#endif +#endif /* USE_MUTEX */
diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h index 0e067c0..081d155 100644 --- a/Utilities/cmcurl/lib/curl_threads.h +++ b/Utilities/cmcurl/lib/curl_threads.h
@@ -25,7 +25,8 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef USE_THREADS_POSIX +#ifdef USE_MUTEX +#ifdef HAVE_THREADS_POSIX # define CURL_THREAD_RETURN_T unsigned int # define CURL_STDCALL # define curl_mutex_t pthread_mutex_t @@ -35,7 +36,10 @@ # define Curl_mutex_acquire(m) pthread_mutex_lock(m) # define Curl_mutex_release(m) pthread_mutex_unlock(m) # define Curl_mutex_destroy(m) pthread_mutex_destroy(m) -#elif defined(USE_THREADS_WIN32) +# define curl_cond_t pthread_cond_t +# define Curl_cond_init(c) pthread_cond_init(c, NULL) +# define Curl_cond_destroy(c) pthread_cond_destroy(c) +#elif defined(_WIN32) # define CURL_THREAD_RETURN_T DWORD # define CURL_STDCALL WINAPI # define curl_mutex_t CRITICAL_SECTION @@ -45,17 +49,29 @@ # define Curl_mutex_acquire(m) EnterCriticalSection(m) # define Curl_mutex_release(m) LeaveCriticalSection(m) # define Curl_mutex_destroy(m) DeleteCriticalSection(m) +# define curl_cond_t CONDITION_VARIABLE +# define Curl_cond_init(c) InitializeConditionVariable(c) +# define Curl_cond_destroy(c) (void)(c) +#else +#error neither HAVE_THREADS_POSIX nor _WIN32 defined #endif -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +void Curl_cond_signal(curl_cond_t *c); +void Curl_cond_wait(curl_cond_t *c, curl_mutex_t *m); +/* Returns CURLE_OPERATION_TIMEDOUT on timeout */ +CURLcode Curl_cond_timedwait(curl_cond_t *c, curl_mutex_t *m, + uint32_t timeout_ms); +#endif /* USE_MUTEX */ -curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T - (CURL_STDCALL *func) (void *), void *arg); +#ifdef USE_THREADS + +curl_thread_t Curl_thread_create( + CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg); void Curl_thread_destroy(curl_thread_t *hnd); int Curl_thread_join(curl_thread_t *hnd); -#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */ +#endif /* USE_THREADS */ #endif /* HEADER_CURL_THREADS_H */
diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c index 4b8734a..c6115cf 100644 --- a/Utilities/cmcurl/lib/curl_trc.c +++ b/Utilities/cmcurl/lib/curl_trc.c
@@ -28,6 +28,7 @@ #include "cfilters.h" #include "multiif.h" +#include "cf-dns.h" #include "cf-socket.h" #include "connect.h" #include "http2.h" @@ -222,6 +223,12 @@ "TIMER", CURL_LOG_LVL_NONE, }; +#ifdef USE_THREADS +struct curl_trc_feat Curl_trc_feat_threads = { + "THREADS", + CURL_LOG_LVL_NONE, +}; +#endif #endif #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -278,6 +285,19 @@ } } +void Curl_trc_feat_infof(struct Curl_easy *data, + struct curl_trc_feat *feat, + const char *fmt, ...) +{ + DEBUGASSERT(feat); + if(Curl_trc_ft_is_verbose(data, feat)) { + va_list ap; + va_start(ap, fmt); + trc_infof(data, feat, NULL, 0, fmt, ap); + va_end(ap); + } +} + static const char * const Curl_trc_timer_names[] = { "100_TIMEOUT", "ASYNC_NAME", @@ -336,7 +356,6 @@ "PENDING", "SETUP", "CONNECT", - "RESOLVING", "CONNECTING", "PROTOCONNECT", "PROTOCONNECTING", @@ -512,6 +531,9 @@ { &Curl_trc_feat_write, TRC_CT_NONE }, { &Curl_trc_feat_dns, TRC_CT_NETWORK }, { &Curl_trc_feat_timer, TRC_CT_NETWORK }, +#ifdef USE_THREADS + { &Curl_trc_feat_threads, TRC_CT_NONE }, +#endif #ifndef CURL_DISABLE_FTP { &Curl_trc_feat_ftp, TRC_CT_PROTOCOL }, #endif @@ -535,6 +557,7 @@ }; static struct trc_cft_def trc_cfts[] = { + { &Curl_cft_dns, TRC_CT_NETWORK }, { &Curl_cft_tcp, TRC_CT_NETWORK }, { &Curl_cft_udp, TRC_CT_NETWORK }, { &Curl_cft_unix, TRC_CT_NETWORK },
diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h index a82c945..b4ae8e5 100644 --- a/Utilities/cmcurl/lib/curl_trc.h +++ b/Utilities/cmcurl/lib/curl_trc.h
@@ -101,6 +101,10 @@ int log_level; }; +void Curl_trc_feat_infof(struct Curl_easy *data, + struct curl_trc_feat *feat, + const char *fmt, ...) CURL_PRINTF(3, 4); + #ifndef CURL_DISABLE_FTP extern struct curl_trc_feat Curl_trc_feat_ftp; void Curl_trc_ftp(struct Curl_easy *data, @@ -304,6 +308,9 @@ extern struct curl_trc_feat Curl_trc_feat_write; extern struct curl_trc_feat Curl_trc_feat_dns; extern struct curl_trc_feat Curl_trc_feat_timer; +#ifdef USE_THREADS +extern struct curl_trc_feat Curl_trc_feat_threads; +#endif #endif #ifndef CURL_DISABLE_VERBOSE_STRINGS
diff --git a/Utilities/cmcurl/lib/curlx/base64.c b/Utilities/cmcurl/lib/curlx/base64.c index a7a6703..7f51576 100644 --- a/Utilities/cmcurl/lib/curlx/base64.c +++ b/Utilities/cmcurl/lib/curlx/base64.c
@@ -149,7 +149,7 @@ pos += 3 - padding; } - /* Zero terminate */ + /* Null-terminate */ *pos = '\0'; /* Return the decoded data */ @@ -213,7 +213,7 @@ } } - /* Zero terminate */ + /* Null-terminate */ *output = '\0'; /* Return the pointer to the new data (allocated memory) */
diff --git a/Utilities/cmcurl/lib/curlx/curlx.h b/Utilities/cmcurl/lib/curlx/curlx.h deleted file mode 100644 index 79cffc8..0000000 --- a/Utilities/cmcurl/lib/curlx/curlx.h +++ /dev/null
@@ -1,52 +0,0 @@ -#ifndef HEADER_CURL_CURLX_H -#define HEADER_CURL_CURLX_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ - -/* - * Defines protos and includes all header files that provide the curlx_* - * functions. The curlx_* functions are not part of the libcurl API, but are - * stand-alone functions whose sources can be built and linked by apps if need - * be. - */ - -#include "curlx/base64.h" /* for curlx_base64* */ -#include "curlx/basename.h" /* for curlx_basename() */ -#include "curlx/dynbuf.h" /* for curlx_dyn_*() */ -#include "curlx/fopen.h" /* for curlx_f*() */ -#include "curlx/inet_ntop.h" /* for curlx_inet_ntop() */ -#include "curlx/inet_pton.h" /* for curlx_inet_pton() */ -#include "curlx/multibyte.h" /* for curlx_convert_*() */ -#include "curlx/nonblock.h" /* for curlx_nonblock() */ -#include "curlx/strcopy.h" /* for curlx_strcopy() */ -#include "curlx/strdup.h" /* for curlx_memdup*() and curlx_tcsdup() */ -#include "curlx/strerr.h" /* for curlx_strerror() */ -#include "curlx/strparse.h" /* for curlx_str_* parsing functions */ -#include "curlx/timediff.h" /* for timediff_t type and related functions */ -#include "curlx/timeval.h" /* for curlx_now type and related functions */ -#include "curlx/version_win32.h" /* for curlx_verify_windows_version() */ -#include "curlx/wait.h" /* for curlx_wait_ms() */ -#include "curlx/winapi.h" /* for curlx_winapi_strerror() */ - -#endif /* HEADER_CURL_CURLX_H */
diff --git a/Utilities/cmcurl/lib/curlx/dynbuf.c b/Utilities/cmcurl/lib/curlx/dynbuf.c index 7045a1f..1e5ea51 100644 --- a/Utilities/cmcurl/lib/curlx/dynbuf.c +++ b/Utilities/cmcurl/lib/curlx/dynbuf.c
@@ -57,7 +57,7 @@ { DEBUGASSERT(s); DEBUGASSERT(s->init == DYNINIT); - Curl_safefree(s->bufr); + curlx_safefree(s->bufr); s->leng = s->allc = 0; } @@ -134,7 +134,7 @@ /* * Specify the size of the tail to keep (number of bytes from the end of the - * buffer). The rest will be dropped. + * buffer). The rest is dropped. */ CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail) { @@ -205,7 +205,7 @@ if(str) { CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str)); - curlx_free(str); + curl_free(str); return result; } /* If we failed, we cleanup the whole buffer and return error */
diff --git a/Utilities/cmcurl/lib/curlx/dynbuf.h b/Utilities/cmcurl/lib/curlx/dynbuf.h index 6f09e13..0cf4a2c 100644 --- a/Utilities/cmcurl/lib/curlx/dynbuf.h +++ b/Utilities/cmcurl/lib/curlx/dynbuf.h
@@ -53,7 +53,7 @@ /* returns 0 on success, -1 on error */ /* The implementation of this function exists in mprintf.c */ -int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save); +int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args); /* Take the buffer out of the dynbuf. Caller has ownership and * dynbuf resets to initial state. */
diff --git a/Utilities/cmcurl/lib/curlx/fopen.c b/Utilities/cmcurl/lib/curlx/fopen.c index 6d0ef7e..6733010 100644 --- a/Utilities/cmcurl/lib/curlx/fopen.c +++ b/Utilities/cmcurl/lib/curlx/fopen.c
@@ -94,7 +94,7 @@ * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path. * * For example 'in' filename255chars in current directory C:\foo\bar is - * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows + * fixed as \\?\C:\foo\bar\filename255chars for 'out' which tells Windows * it is ok to access that filename even though the actual full path is longer * than 260 chars. * @@ -296,7 +296,7 @@ int curlx_win32_open(const char *filename, int oflag, ...) { int pmode = 0; - int result = -1; + int res = -1; TCHAR *fixed = NULL; const TCHAR *target = NULL; @@ -316,7 +316,7 @@ target = fixed; else target = filename_w; - errno = _wsopen_s(&result, target, oflag, _SH_DENYNO, pmode); + errno = _wsopen_s(&res, target, oflag, _SH_DENYNO, pmode); CURLX_FREE(filename_w); } else @@ -327,16 +327,16 @@ target = fixed; else target = filename; - errno = _sopen_s(&result, target, oflag, _SH_DENYNO, pmode); + errno = _sopen_s(&res, target, oflag, _SH_DENYNO, pmode); #endif CURLX_FREE(fixed); - return result; + return res; } FILE *curlx_win32_fopen(const char *filename, const char *mode) { - FILE *result = NULL; + FILE *file = NULL; TCHAR *fixed = NULL; const TCHAR *target = NULL; @@ -348,7 +348,7 @@ target = fixed; else target = filename_w; - result = _wfsopen(target, mode_w, _SH_DENYNO); + file = _wfsopen(target, mode_w, _SH_DENYNO); } else /* !checksrc! disable ERRNOVAR 1 */ @@ -360,11 +360,11 @@ target = fixed; else target = filename; - result = _fsopen(target, mode, _SH_DENYNO); + file = _fsopen(target, mode, _SH_DENYNO); #endif CURLX_FREE(fixed); - return result; + return file; } #if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR < 5) @@ -374,7 +374,7 @@ FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fp) { - FILE *result = NULL; + FILE *file = NULL; TCHAR *fixed = NULL; const TCHAR *target = NULL; @@ -386,7 +386,7 @@ target = fixed; else target = filename_w; - errno = _wfreopen_s(&result, target, mode_w, fp); + errno = _wfreopen_s(&file, target, mode_w, fp); } else /* !checksrc! disable ERRNOVAR 1 */ @@ -398,16 +398,16 @@ target = fixed; else target = filename; - errno = freopen_s(&result, target, mode, fp); + errno = freopen_s(&file, target, mode, fp); #endif CURLX_FREE(fixed); - return result; + return file; } int curlx_win32_stat(const char *path, curlx_struct_stat *buffer) { - int result = -1; + int res = -1; TCHAR *fixed = NULL; const TCHAR *target = NULL; @@ -418,7 +418,7 @@ target = fixed; else target = path_w; - result = _wstati64(target, buffer); + res = _wstati64(target, buffer); curlx_free(path_w); } else @@ -429,17 +429,17 @@ target = fixed; else target = path; - result = _stati64(target, buffer); + res = _stati64(target, buffer); #endif CURLX_FREE(fixed); - return result; + return res; } #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES) || \ !defined(CURL_DISABLE_ALTSVC) /* rename() on Windows does not overwrite, so we cannot use it here. - MoveFileEx() will overwrite and is usually atomic but fails when there are + MoveFileEx() does overwrite and is usually atomic but fails when there are open handles to the file. */ int curlx_win32_rename(const char *oldpath, const char *newpath) {
diff --git a/Utilities/cmcurl/lib/curlx/fopen.h b/Utilities/cmcurl/lib/curlx/fopen.h index d89830e..b64fbf6 100644 --- a/Utilities/cmcurl/lib/curlx/fopen.h +++ b/Utilities/cmcurl/lib/curlx/fopen.h
@@ -47,7 +47,7 @@ #define curlx_fstat _fstati64 #define curlx_struct_stat struct _stati64 FILE *curlx_win32_fopen(const char *filename, const char *mode); -FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fh); +FILE *curlx_win32_freopen(const char *filename, const char *mode, FILE *fp); int curlx_win32_stat(const char *path, curlx_struct_stat *buffer); int curlx_win32_open(const char *filename, int oflag, ...); int curlx_win32_rename(const char *oldpath, const char *newpath);
diff --git a/Utilities/cmcurl/lib/curlx/inet_ntop.h b/Utilities/cmcurl/lib/curlx/inet_ntop.h index 2e3419f..ba8299f 100644 --- a/Utilities/cmcurl/lib/curlx/inet_ntop.h +++ b/Utilities/cmcurl/lib/curlx/inet_ntop.h
@@ -36,15 +36,15 @@ #include <arpa/inet.h> #endif #ifdef __AMIGA__ -#define curlx_inet_ntop(af, addr, buf, size) \ - (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)(buf), \ +#define curlx_inet_ntop(af, src, buf, size) \ + (char *)inet_ntop(af, CURL_UNCONST(src), (unsigned char *)(buf), \ (curl_socklen_t)(size)) #else -#define curlx_inet_ntop(af, addr, buf, size) \ - inet_ntop(af, addr, buf, (curl_socklen_t)(size)) +#define curlx_inet_ntop(af, src, buf, size) \ + inet_ntop(af, src, buf, (curl_socklen_t)(size)) #endif #else -char *curlx_inet_ntop(int af, const void *addr, char *buf, size_t size); +char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size); #endif /* HAVE_INET_NTOP */ #endif /* HEADER_CURL_INET_NTOP_H */
diff --git a/Utilities/cmcurl/lib/curlx/inet_pton.c b/Utilities/cmcurl/lib/curlx/inet_pton.c index d3c53fb..7994f25 100644 --- a/Utilities/cmcurl/lib/curlx/inet_pton.c +++ b/Utilities/cmcurl/lib/curlx/inet_pton.c
@@ -170,7 +170,7 @@ if(colonp) { /* * Since some memmove()'s erroneously fail to handle - * overlapping regions, we will do the shift by hand. + * overlapping regions, we do the shift by hand. */ const ssize_t n = tp - colonp; ssize_t i;
diff --git a/Utilities/cmcurl/lib/curlx/snprintf.c b/Utilities/cmcurl/lib/curlx/snprintf.c index c733252..911c42e 100644 --- a/Utilities/cmcurl/lib/curlx/snprintf.c +++ b/Utilities/cmcurl/lib/curlx/snprintf.c
@@ -34,13 +34,13 @@ if(!maxlen) return; va_start(ap, fmt); -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif /* !checksrc! disable BANNEDFUNC 1 */ (void)vsnprintf(buf, maxlen, fmt, ap); -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif buf[maxlen - 1] = 0;
diff --git a/Utilities/cmcurl/lib/curlx/strcopy.c b/Utilities/cmcurl/lib/curlx/strcopy.c index da137c3..3e58ea2 100644 --- a/Utilities/cmcurl/lib/curlx/strcopy.c +++ b/Utilities/cmcurl/lib/curlx/strcopy.c
@@ -30,10 +30,10 @@ * * Provide the target buffer @dest and size of the target buffer @dsize, If * the source string @src with its *string length* @slen fits in the target - * buffer it will be copied there - including storing a null terminator. + * buffer it is copied there - including storing a null terminator. * * If the target buffer is too small, the copy is not performed but if the - * target buffer has a non-zero size it will get a null terminator stored. + * target buffer has a non-zero size it gets a null terminator stored. */ void curlx_strcopy(char *dest, /* destination buffer */ size_t dsize, /* size of target buffer */
diff --git a/Utilities/cmcurl/lib/curlx/strerr.c b/Utilities/cmcurl/lib/curlx/strerr.c index 91a329e..b53173c 100644 --- a/Utilities/cmcurl/lib/curlx/strerr.c +++ b/Utilities/cmcurl/lib/curlx/strerr.c
@@ -263,7 +263,7 @@ *buf = '\0'; #ifdef _WIN32 - if((!strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) && + if((strerror_s(buf, buflen, err) || !strcmp(buf, "Unknown error")) && #ifdef USE_WINSOCK !get_winsock_error(err, buf, buflen) && #endif
diff --git a/Utilities/cmcurl/lib/curlx/strparse.c b/Utilities/cmcurl/lib/curlx/strparse.c index 138b3df..7866b20 100644 --- a/Utilities/cmcurl/lib/curlx/strparse.c +++ b/Utilities/cmcurl/lib/curlx/strparse.c
@@ -35,6 +35,15 @@ out->len = len; } +/* remove bytes from the end of the string, never remove more bytes than what + the string holds! */ +void curlx_str_trim(struct Curl_str *out, size_t len) +{ + DEBUGASSERT(out); + DEBUGASSERT(out->len >= len); + out->len -= len; +} + /* Get a word until the first DELIM or end of string. At least one byte long. return non-zero on error */ int curlx_str_until(const char **linep, struct Curl_str *out,
diff --git a/Utilities/cmcurl/lib/curlx/strparse.h b/Utilities/cmcurl/lib/curlx/strparse.h index 514203d..c7801b2 100644 --- a/Utilities/cmcurl/lib/curlx/strparse.h +++ b/Utilities/cmcurl/lib/curlx/strparse.h
@@ -44,6 +44,7 @@ void curlx_str_init(struct Curl_str *out); void curlx_str_assign(struct Curl_str *out, const char *str, size_t len); +void curlx_str_trim(struct Curl_str *out, size_t len); #define curlx_str(x) ((x)->str) #define curlx_strlen(x) ((x)->len)
diff --git a/Utilities/cmcurl/lib/curlx/timeval.c b/Utilities/cmcurl/lib/curlx/timeval.c index 435804c..2363e60 100644 --- a/Utilities/cmcurl/lib/curlx/timeval.c +++ b/Utilities/cmcurl/lib/curlx/timeval.c
@@ -166,6 +166,8 @@ */ pnow->tv_sec = time(NULL); pnow->tv_usec = 0; + if(!pnow->tv_sec) /* avoid a `now` fully zero */ + pnow->tv_usec = 1; } #endif
diff --git a/Utilities/cmcurl/lib/curlx/wait.c b/Utilities/cmcurl/lib/curlx/wait.c index 5278426..e50a0f0 100644 --- a/Utilities/cmcurl/lib/curlx/wait.c +++ b/Utilities/cmcurl/lib/curlx/wait.c
@@ -46,7 +46,7 @@ * wait on, being used to delay execution. Winsock select() and poll() timeout * mechanisms need a valid socket descriptor in a not null file descriptor set * to work. Waiting indefinitely with this function is not allowed, a zero or - * negative timeout value will return immediately. Timeout resolution, + * negative timeout value is returned immediately. Timeout resolution, * accuracy, as well as maximum supported value is system dependent, neither * factor is a critical issue for the intended use of this function in the * library.
diff --git a/Utilities/cmcurl/lib/curlx/winapi.c b/Utilities/cmcurl/lib/curlx/winapi.c index 3417df2..f025ca4 100644 --- a/Utilities/cmcurl/lib/curlx/winapi.c +++ b/Utilities/cmcurl/lib/curlx/winapi.c
@@ -44,7 +44,7 @@ return NULL; /* We return the local codepage version of the error string because if it is - output to the user's terminal it will likely be with functions which + output to the user's terminal, it is likely done with functions which expect the local codepage (eg fprintf, failf, infof). */ if(!FormatMessageA((FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err,
diff --git a/Utilities/cmcurl/lib/cw-out.c b/Utilities/cmcurl/lib/cw-out.c index 554c32e..2af074e 100644 --- a/Utilities/cmcurl/lib/cw-out.c +++ b/Utilities/cmcurl/lib/cw-out.c
@@ -31,7 +31,7 @@ #include "transfer.h" #include "cw-out.h" #include "cw-pause.h" - +#include "progress.h" /** * OVERALL DESIGN of this client writer @@ -211,7 +211,7 @@ } else if(nwritten != blen) { failf(data, "Failure writing output to destination, " - "passed %zu returned %zd", blen, nwritten); + "passed %zu returned %zu", blen, nwritten); return CURLE_WRITE_ERROR; } *pnwritten = nwritten; @@ -228,8 +228,8 @@ curl_write_callback wcb = NULL; void *wcb_data; size_t max_write, min_write; - size_t wlen, nwritten; - CURLcode result; + size_t wlen, nwritten = 0; + CURLcode result = CURLE_OK; /* If we errored once, we do not invoke the client callback again */ if(ctx->errored) @@ -253,10 +253,15 @@ if(!flush_all && blen < min_write) break; wlen = max_write ? CURLMIN(blen, max_write) : blen; - result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype, - buf, wlen, &nwritten); + if(otype == CW_OUT_BODY) + result = Curl_pgrs_deliver_check(data, wlen); + if(!result) + result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype, + buf, wlen, &nwritten); if(result) return result; + if(otype == CW_OUT_BODY) + Curl_pgrs_deliver_inc(data, nwritten); *pconsumed += nwritten; blen -= nwritten; buf += nwritten; @@ -350,8 +355,8 @@ } /* if we do not have a buffer, or it is of another type, make a new one. - * And for CW_OUT_HDS always make a new one, so we "replay" headers - * exactly as they came in */ + * For CW_OUT_HDS always make a new one, so we "replay" headers exactly + * as they came in */ if(!ctx->buf || (ctx->buf->type != otype) || (otype == CW_OUT_HDS)) { struct cw_out_buf *cwbuf = cw_out_buf_create(otype); if(!cwbuf)
diff --git a/Utilities/cmcurl/lib/dict.c b/Utilities/cmcurl/lib/dict.c index fcdea91..7b83c6c 100644 --- a/Utilities/cmcurl/lib/dict.c +++ b/Utilities/cmcurl/lib/dict.c
@@ -64,7 +64,6 @@ #define DICT_DEFINE2 "/D:" #define DICT_DEFINE3 "/LOOKUP:" - #define DYN_DICT_WORD 10000 static char *unescape_word(const char *input) { @@ -275,7 +274,7 @@ /* * DICT protocol */ -static const struct Curl_protocol Curl_protocol_dict = { +const struct Curl_protocol Curl_protocol_dict = { ZERO_NULL, /* setup_connection */ dict_do, /* do_it */ ZERO_NULL, /* done */ @@ -290,25 +289,9 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif /* CURL_DISABLE_DICT */ - -/* - * DICT protocol handler. - */ -const struct Curl_scheme Curl_scheme_dict = { - "dict", /* scheme */ -#ifdef CURL_DISABLE_DICT - ZERO_NULL, -#else - &Curl_protocol_dict, -#endif - CURLPROTO_DICT, /* protocol */ - CURLPROTO_DICT, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */ - PORT_DICT, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/dict.h b/Utilities/cmcurl/lib/dict.h index b5d6947..5bdfcff 100644 --- a/Utilities/cmcurl/lib/dict.h +++ b/Utilities/cmcurl/lib/dict.h
@@ -23,6 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_dict; +#ifndef CURL_DISABLE_DICT +extern const struct Curl_protocol Curl_protocol_dict; +#endif #endif /* HEADER_CURL_DICT_H */
diff --git a/Utilities/cmcurl/lib/dnscache.c b/Utilities/cmcurl/lib/dnscache.c new file mode 100644 index 0000000..20f6b21 --- /dev/null +++ b/Utilities/cmcurl/lib/dnscache.c
@@ -0,0 +1,873 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETINET_IN6_H +#include <netinet/in6.h> +#endif +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef __VMS +#include <in.h> +#include <inet.h> +#endif + +#include "urldata.h" +#include "curl_addrinfo.h" +#include "curl_share.h" +#include "curl_trc.h" +#include "dnscache.h" +#include "hash.h" +#include "hostip.h" +#include "httpsrr.h" +#include "progress.h" +#include "rand.h" +#include "strcase.h" +#include "curlx/inet_ntop.h" +#include "curlx/inet_pton.h" +#include "curlx/strcopy.h" +#include "curlx/strparse.h" + +#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */ + +#define MAX_DNS_CACHE_SIZE 29999 + +static void dnscache_entry_free(struct Curl_dns_entry *dns) +{ + Curl_freeaddrinfo(dns->addr); +#ifdef USE_HTTPSRR + if(dns->hinfo) { + Curl_httpsrr_cleanup(dns->hinfo); + curlx_free(dns->hinfo); + } +#endif + curlx_free(dns); +} + +/* + * Create a hostcache id string for the provided host + port, to be used by + * the DNS caching. Without alloc. Return length of the id string. + */ +static size_t create_dnscache_id(const char *name, + size_t nlen, /* 0 or actual name length */ + uint16_t port, char *ptr, size_t buflen) +{ + size_t len = nlen ? nlen : strlen(name); + DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); + if(len > (buflen - 7)) + len = buflen - 7; + /* store and lower case the name */ + Curl_strntolower(ptr, name, len); + return curl_msnprintf(&ptr[len], 7, ":%u", port) + len; +} + +struct dnscache_prune_data { + struct curltime now; + timediff_t oldest_ms; /* oldest time in cache not pruned. */ + timediff_t max_age_ms; +}; + +/* + * This function is set as a callback to be called for every entry in the DNS + * cache when we want to prune old unused entries. + * + * Returning non-zero means remove the entry, return 0 to keep it in the + * cache. + */ +static int dnscache_entry_is_stale(void *datap, void *hc) +{ + struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap; + struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc; + + if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) { + /* get age in milliseconds */ + timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp); + if(!dns->addr) + age *= 2; /* negative entries age twice as fast */ + if(age >= prune->max_age_ms) + return TRUE; + if(age > prune->oldest_ms) + prune->oldest_ms = age; + } + return FALSE; +} + +/* + * Prune the DNS cache. This assumes that a lock has already been taken. + * Returns the 'age' of the oldest still kept entry - in milliseconds. + */ +static timediff_t dnscache_prune(struct Curl_hash *hostcache, + timediff_t cache_timeout_ms, + struct curltime now) +{ + struct dnscache_prune_data user; + + user.max_age_ms = cache_timeout_ms; + user.now = now; + user.oldest_ms = 0; + + Curl_hash_clean_with_criterium(hostcache, + (void *)&user, + dnscache_entry_is_stale); + + return user.oldest_ms; +} + +static struct Curl_dnscache *dnscache_get(struct Curl_easy *data) +{ + if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) + return &data->share->dnscache; + if(data->multi) + return &data->multi->dnscache; + return NULL; +} + +static void dnscache_lock(struct Curl_easy *data, + struct Curl_dnscache *dnscache) +{ + if(data->share && dnscache == &data->share->dnscache) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); +} + +static void dnscache_unlock(struct Curl_easy *data, + struct Curl_dnscache *dnscache) +{ + if(data->share && dnscache == &data->share->dnscache) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); +} + +/* + * Library-wide function for pruning the DNS cache. This function takes and + * returns the appropriate locks. + */ +void Curl_dnscache_prune(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + /* the timeout may be set -1 (forever) */ + timediff_t timeout_ms = data->set.dns_cache_timeout_ms; + + if(!dnscache || (timeout_ms == -1)) + /* NULL hostcache means we cannot do it */ + return; + + dnscache_lock(data, dnscache); + + do { + /* Remove outdated and unused entries from the hostcache */ + timediff_t oldest_ms = + dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data)); + + if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE) + /* prune the ones over half this age */ + timeout_ms = oldest_ms / 2; + else + break; + + /* if the cache size is still too big, use the oldest age as new prune + limit */ + } while(timeout_ms); + + dnscache_unlock(data, dnscache); +} + +void Curl_dnscache_clear(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + if(dnscache) { + dnscache_lock(data, dnscache); + Curl_hash_clean(&dnscache->entries); + dnscache_unlock(data, dnscache); + } +} + +/* lookup address, returns entry if found and not stale */ +static CURLcode fetch_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pdns) +{ + struct Curl_dns_entry *dns = NULL; + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + CURLcode result = CURLE_OK; + + *pdns = NULL; + if(!dnscache) + return CURLE_OK; + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(hostname, 0, port, + entry_id, sizeof(entry_id)); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + + /* No entry found in cache, check if we might have a wildcard entry */ + if(!dns && data->state.wildcard_resolve) { + entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id)); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + } + + if(dns && (data->set.dns_cache_timeout_ms != -1)) { + /* See whether the returned entry is stale. Done before we release lock */ + struct dnscache_prune_data user; + + user.now = *Curl_pgrs_now(data); + user.max_age_ms = data->set.dns_cache_timeout_ms; + user.oldest_ms = 0; + + if(dnscache_entry_is_stale(&user, dns)) { + infof(data, "Hostname in DNS cache was stale, zapped"); + dns = NULL; /* the memory deallocation is being handled by the hash */ + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + } + } + + if(dns) { + if((dns->dns_queries & dns_queries) != dns_queries) { + /* The entry does not cover all wanted DNS queries, a miss. */ + dns = NULL; + } + else if(!(dns->dns_responses & dns_queries)) { + /* The entry has no responses for the wanted DNS queries. */ + CURL_TRC_DNS(data, "cache entry does not have type=%s addresses", + Curl_resolv_query_str(dns_queries)); + dns = NULL; + result = CURLE_COULDNT_RESOLVE_HOST; + } + } + + if(dns && !dns->addr) { /* negative entry */ + dns = NULL; + result = CURLE_COULDNT_RESOLVE_HOST; + } + *pdns = dns; + return result; +} + +/* + * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. + * + * Curl_resolv() checks initially and multi_runsingle() checks each time + * it discovers the handle in the state WAITRESOLVE whether the hostname + * has already been resolved and the address has already been stored in + * the DNS cache. This short circuits waiting for a lot of pending + * lookups for the same hostname requested by different handles. + * + * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after + * use, or we will leak memory! + */ +CURLcode Curl_dnscache_get(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pentry) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns = NULL; + CURLcode result = CURLE_OK; + + dnscache_lock(data, dnscache); + result = fetch_addr(data, dnscache, dns_queries, hostname, port, &dns); + if(!result && dns) + dns->refcount++; /* we pass out a reference */ + else if(result) { + DEBUGASSERT(!dns); + dns = NULL; + } + dnscache_unlock(data, dnscache); + + *pentry = dns; + return result; +} + +#ifndef CURL_DISABLE_SHUFFLE_DNS +/* + * Return # of addresses in a Curl_addrinfo struct + */ +static int num_addresses(const struct Curl_addrinfo *addr) +{ + int i = 0; + while(addr) { + addr = addr->ai_next; + i++; + } + return i; +} + +/* + * dns_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' + * struct by re-linking its linked list. + * + * The addr argument should be the address of a pointer to the head node of a + * `Curl_addrinfo` list and it will be modified to point to the new head after + * shuffling. + * + * Not declared static only to make it easy to use in a unit test! + * + * @unittest 1608 + */ +UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data, + struct Curl_addrinfo **addr); +UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data, + struct Curl_addrinfo **addr) +{ + CURLcode result = CURLE_OK; + const int num_addrs = num_addresses(*addr); + + if(num_addrs > 1) { + struct Curl_addrinfo **nodes; + CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs); + + nodes = curlx_malloc(num_addrs * sizeof(*nodes)); + if(nodes) { + int i; + unsigned int *rnd; + const size_t rnd_size = num_addrs * sizeof(*rnd); + + /* build a plain array of Curl_addrinfo pointers */ + nodes[0] = *addr; + for(i = 1; i < num_addrs; i++) { + nodes[i] = nodes[i - 1]->ai_next; + } + + rnd = curlx_malloc(rnd_size); + if(rnd) { + /* Fisher-Yates shuffle */ + if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { + struct Curl_addrinfo *swap_tmp; + for(i = num_addrs - 1; i > 0; i--) { + swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)]; + nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i]; + nodes[i] = swap_tmp; + } + + /* relink list in the new order */ + for(i = 1; i < num_addrs; i++) { + nodes[i - 1]->ai_next = nodes[i]; + } + + nodes[num_addrs - 1]->ai_next = NULL; + *addr = nodes[0]; + } + curlx_free(rnd); + } + else + result = CURLE_OUT_OF_MEMORY; + curlx_free(nodes); + } + else + result = CURLE_OUT_OF_MEMORY; + } + return result; +} +#endif + +static bool dnscache_ai_has_family(struct Curl_addrinfo *ai, + int ai_family) +{ + for(; ai; ai = ai->ai_next) { + if(ai->ai_family == ai_family) + return TRUE; + } + return FALSE; +} + +static struct Curl_dns_entry *dnscache_entry_create( + struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + size_t hostlen, + uint16_t port, + bool permanent) +{ + struct Curl_dns_entry *dns = NULL; + + /* Create a new cache entry, struct already has the hostname NUL */ + dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen); + if(!dns) + goto out; + + dns->refcount = 1; /* the cache has the first reference */ + dns->dns_queries = dns_queries; + dns->port = port; + if(hostlen) + memcpy(dns->hostname, hostname, hostlen); + + if(permanent) { + dns->timestamp.tv_sec = 0; /* an entry that never goes stale */ + dns->timestamp.tv_usec = 0; /* an entry that never goes stale */ + } + else { + dns->timestamp = *Curl_pgrs_now(data); + } + + /* Take the given address lists into the entry */ + if(paddr1 && *paddr1) { + dns->addr = *paddr1; + *paddr1 = NULL; + } + if(paddr2 && *paddr2) { + struct Curl_addrinfo **phead = &dns->addr; + while(*phead) + phead = &(*phead)->ai_next; + *phead = *paddr2; + *paddr2 = NULL; + } + + if((dns_queries & CURL_DNSQ_A) && + dnscache_ai_has_family(dns->addr, PF_INET)) + dns->dns_responses |= CURL_DNSQ_A; + +#ifdef USE_IPV6 + if((dns_queries & CURL_DNSQ_AAAA) && + dnscache_ai_has_family(dns->addr, PF_INET6)) + dns->dns_responses |= CURL_DNSQ_AAAA; +#endif /* USE_IPV6 */ + +#ifndef CURL_DISABLE_SHUFFLE_DNS + /* shuffle addresses if requested */ + if(data->set.dns_shuffle_addresses && dns->addr) { + CURLcode result = dns_shuffle_addr(data, &dns->addr); + if(result) { + /* free without lock, we are the sole owner */ + dnscache_entry_free(dns); + dns = NULL; + goto out; + } + } +#else + (void)data; +#endif + +out: + if(paddr1 && *paddr1) { + Curl_freeaddrinfo(*paddr1); + *paddr1 = NULL; + } + if(paddr2 && *paddr2) { + Curl_freeaddrinfo(*paddr2); + *paddr2 = NULL; + } + return dns; +} + +struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + uint16_t port) +{ + return dnscache_entry_create(data, dns_queries, paddr, NULL, hostname, + hostname ? strlen(hostname) : 0, + port, FALSE); +} + +struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + uint16_t port) +{ + return dnscache_entry_create(data, dns_queries, paddr1, paddr2, hostname, + hostname ? strlen(hostname) : 0, + port, FALSE); +} + +#ifdef USE_HTTPSRR +void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, + struct Curl_https_rrinfo *hinfo) +{ + /* only do this when this is the only reference */ + DEBUGASSERT(dns->refcount == 1); + /* it should have been in the queries */ + DEBUGASSERT(dns->dns_queries & CURL_DNSQ_HTTPS); + if(dns->hinfo) { + Curl_httpsrr_cleanup(dns->hinfo); + curlx_free(dns->hinfo); + } + dns->hinfo = hinfo; + dns->dns_responses |= CURL_DNSQ_HTTPS; +} +#endif /* USE_HTTPSRR */ + +static struct Curl_dns_entry *dnscache_add_addr(struct Curl_easy *data, + struct Curl_dnscache *dnscache, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + size_t hlen, + uint16_t port, + bool permanent) +{ + char entry_id[MAX_HOSTCACHE_LEN]; + size_t entry_len; + struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns2; + + dns = dnscache_entry_create(data, dns_queries, paddr, NULL, + hostname, hlen, port, permanent); + if(!dns) + return NULL; + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(hostname, hlen, port, + entry_id, sizeof(entry_id)); + + /* Store the resolved data in our DNS cache. */ + dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, + (void *)dns); + if(!dns2) { + dnscache_entry_free(dns); + return NULL; + } + + dns = dns2; + dns->refcount++; /* mark entry as in-use */ + return dns; +} + +CURLcode Curl_dnscache_add(struct Curl_easy *data, + struct Curl_dns_entry *entry) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + char id[MAX_HOSTCACHE_LEN]; + size_t idlen; + + if(!dnscache) + return CURLE_FAILED_INIT; + /* Create an entry id, based upon the hostname and port */ + idlen = create_dnscache_id(entry->hostname, 0, entry->port, id, sizeof(id)); + + /* Store the resolved data in our DNS cache and up ref count */ + dnscache_lock(data, dnscache); + if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) { + dnscache_unlock(data, dnscache); + return CURLE_OUT_OF_MEMORY; + } + entry->refcount++; + dnscache_unlock(data, dnscache); + return CURLE_OK; +} + +CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, + uint8_t dns_queries, + const char *host, + uint16_t port) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns; + DEBUGASSERT(dnscache); + if(!dnscache) + return CURLE_FAILED_INIT; + + dnscache_lock(data, dnscache); + + /* put this new host in the cache */ + dns = dnscache_add_addr(data, dnscache, dns_queries, NULL, + host, strlen(host), port, FALSE); + if(dns) { + /* release the returned reference; the cache itself will keep the + * entry alive: */ + dns->refcount--; + dnscache_unlock(data, dnscache); + CURL_TRC_DNS(data, "cache negative name resolve for %s:%d type=%s", + host, port, Curl_resolv_query_str(dns_queries)); + return CURLE_OK; + } + dnscache_unlock(data, dnscache); + return CURLE_OUT_OF_MEMORY; +} + +struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, + struct Curl_dns_entry *dns) +{ + if(!dns) + return NULL; + else { + struct Curl_dnscache *dnscache = dnscache_get(data); + dnscache_lock(data, dnscache); + dns->refcount++; + dnscache_unlock(data, dnscache); + return dns; + } +} + +/* + * Curl_dns_entry_unlink() releases a reference to the given cached DNS entry. + * When the reference count reaches 0, the entry is destroyed. It is important + * that only one unlink is made for each Curl_resolv() call. + * + * May be called with 'data' == NULL for global cache. + */ +void Curl_dns_entry_unlink(struct Curl_easy *data, + struct Curl_dns_entry **pdns) +{ + if(*pdns) { + struct Curl_dnscache *dnscache = dnscache_get(data); + struct Curl_dns_entry *dns = *pdns; + *pdns = NULL; + dnscache_lock(data, dnscache); + dns->refcount--; + if(dns->refcount == 0) + dnscache_entry_free(dns); + dnscache_unlock(data, dnscache); + } +} + +static void dnscache_entry_dtor(void *entry) +{ + struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry; + DEBUGASSERT(dns && (dns->refcount > 0)); + dns->refcount--; + if(dns->refcount == 0) + dnscache_entry_free(dns); +} + +/* + * Curl_dnscache_init() inits a new DNS cache. + */ +void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size) +{ + Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare, + dnscache_entry_dtor); +} + +void Curl_dnscache_destroy(struct Curl_dnscache *dns) +{ + Curl_hash_destroy(&dns->entries); +} + +CURLcode Curl_loadhostpairs(struct Curl_easy *data) +{ + struct Curl_dnscache *dnscache = dnscache_get(data); + struct curl_slist *hostp; + + if(!dnscache) + return CURLE_FAILED_INIT; + + /* Default is no wildcard found */ + data->state.wildcard_resolve = FALSE; + + for(hostp = data->state.resolve; hostp; hostp = hostp->next) { + char entry_id[MAX_HOSTCACHE_LEN]; + const char *host = hostp->data; + struct Curl_str source; + if(!host) + continue; + if(*host == '-') { + curl_off_t num = 0; + size_t entry_len; + host++; + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']') || + curlx_str_single(&host, ':')) + continue; + } + else { + if(curlx_str_until(&host, &source, 4096, ':') || + curlx_str_single(&host, ':')) { + continue; + } + } + + if(!curlx_str_number(&host, &num, 0xffff)) { + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(curlx_str(&source), + curlx_strlen(&source), (uint16_t)num, + entry_id, sizeof(entry_id)); + dnscache_lock(data, dnscache); + /* delete entry, ignore if it did not exist */ + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + dnscache_unlock(data, dnscache); + } + } + else { + struct Curl_dns_entry *dns; + struct Curl_addrinfo *head = NULL, *tail = NULL; + size_t entry_len; + char address[64]; + curl_off_t tmpofft = 0; + uint16_t port = 0; + bool permanent = TRUE; + bool error = TRUE; + VERBOSE(const char *addresses = NULL); + + if(*host == '+') { + host++; + permanent = FALSE; + } + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']')) + continue; + } + else { + if(curlx_str_until(&host, &source, 4096, ':')) + continue; + } + if(curlx_str_single(&host, ':') || + curlx_str_number(&host, &tmpofft, 0xffff) || + curlx_str_single(&host, ':')) + goto err; + port = (uint16_t)tmpofft; + + VERBOSE(addresses = host); + + /* start the address section */ + while(*host) { + struct Curl_str target; + struct Curl_addrinfo *ai; + CURLcode result; + + if(!curlx_str_single(&host, '[')) { + if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') || + curlx_str_single(&host, ']')) + goto err; + } + else { + if(curlx_str_until(&host, &target, 4096, ',')) { + if(curlx_str_single(&host, ',')) + goto err; + /* survive nothing but a comma */ + continue; + } + } +#ifndef USE_IPV6 + if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) { + infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.", + (int)curlx_strlen(&target), curlx_str(&target)); + if(curlx_str_single(&host, ',')) + goto err; + continue; + } +#endif + + if(curlx_strlen(&target) >= sizeof(address)) + goto err; + + memcpy(address, curlx_str(&target), curlx_strlen(&target)); + address[curlx_strlen(&target)] = '\0'; + + result = Curl_str2addr(address, port, &ai); + if(result) { + infof(data, "Resolve address '%s' found illegal", address); + goto err; + } + + if(tail) { + tail->ai_next = ai; + tail = tail->ai_next; + } + else { + head = tail = ai; + } + if(curlx_str_single(&host, ',')) + break; + } + + if(!head) + goto err; + + error = FALSE; +err: + if(error) { + failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data); + Curl_freeaddrinfo(head); + return CURLE_SETOPT_OPTION_SYNTAX; + } + + /* Create an entry id, based upon the hostname and port */ + entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source), + port, entry_id, sizeof(entry_id)); + + dnscache_lock(data, dnscache); + + /* See if it is already in our dns cache */ + dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); + + if(dns) { + infof(data, "RESOLVE %.*s:%u - old addresses discarded", + (int)curlx_strlen(&source), + curlx_str(&source), port); + /* delete old entry, there are two reasons for this + 1. old entry may have different addresses. + 2. even if entry with correct addresses is already in the cache, + but if it is close to expire, then by the time next http + request is made, it can get expired and pruned because old + entry is not necessarily marked as permanent. + 3. when adding a non-permanent entry, we want it to remove and + replace an existing permanent entry. + 4. when adding a non-permanent entry, we want it to get a "fresh" + timeout that starts _now_. */ + + Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); + } + + /* put this new host in the cache, an overridy for ALL dns queries */ + dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL, + &head, curlx_str(&source), + curlx_strlen(&source), port, permanent); + if(dns) + /* release the returned reference; the cache itself will keep the + * entry alive: */ + dns->refcount--; + + dnscache_unlock(data, dnscache); + + if(!dns) + return CURLE_OUT_OF_MEMORY; + + infof(data, "Added %.*s:%u:%s to DNS cache%s", + (int)curlx_strlen(&source), curlx_str(&source), port, addresses, + permanent ? "" : " (non-permanent)"); + + /* Wildcard hostname */ + if(curlx_str_casecompare(&source, "*")) { + infof(data, "RESOLVE *:%u using wildcard", port); + data->state.wildcard_resolve = TRUE; + } + } + } + data->state.resolve = NULL; /* dealt with now */ + + return CURLE_OK; +}
diff --git a/Utilities/cmcurl/lib/dnscache.h b/Utilities/cmcurl/lib/dnscache.h new file mode 100644 index 0000000..ebe25f6 --- /dev/null +++ b/Utilities/cmcurl/lib/dnscache.h
@@ -0,0 +1,143 @@ +#ifndef HEADER_CURL_DNSCACHE_H +#define HEADER_CURL_DNSCACHE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "hash.h" +#include "curlx/timeval.h" + +struct addrinfo; +struct hostent; +struct Curl_easy; +struct connectdata; +struct easy_pollset; +struct Curl_https_rrinfo; +struct Curl_multi; + +struct Curl_dns_entry { + struct Curl_addrinfo *addr; +#ifdef USE_HTTPSRR + struct Curl_https_rrinfo *hinfo; +#endif + /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ + struct curltime timestamp; + /* reference counter, entry is freed on reaching 0 */ + uint32_t refcount; + /* hostname port number that resolved to addr. */ + uint16_t port; + uint8_t dns_queries; /* CURL_DNSQ_* type of queries performed for this */ + uint8_t dns_responses; /* CURL_DNSQ_* type this entry has responses for */ + /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ + char hostname[1]; +}; + +/* + * Create a `Curl_dns_entry` with a reference count of 1. + * Use `Curl_dns_entry_unlink()` to release your hold on it. + * + * The call takes ownership of `paddr`, even in case of failure, and always + * clears `*paddr`. It makes a copy of `hostname`. + * + * Returns entry or NULL on OOM. + */ +struct Curl_dns_entry *Curl_dnscache_mk_entry(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr, + const char *hostname, + uint16_t port); + +struct Curl_dns_entry *Curl_dnscache_mk_entry2(struct Curl_easy *data, + uint8_t dns_queries, + struct Curl_addrinfo **paddr1, + struct Curl_addrinfo **paddr2, + const char *hostname, + uint16_t port); + +#ifdef USE_HTTPSRR +void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns, + struct Curl_https_rrinfo *hinfo); +#endif /* USE_HTTPSRR */ + +/* Increase the ref counter and return it for storing in another place. + * May be called with NULL, in which case it returns NULL. */ +struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data, + struct Curl_dns_entry *dns); + +/* unlink a dns entry, frees all resources if it was the last reference. + * Always clears `*pdns`` */ +void Curl_dns_entry_unlink(struct Curl_easy *data, + struct Curl_dns_entry **pdns); + +struct Curl_dnscache { + struct Curl_hash entries; +}; + +/* init a new dns cache */ +void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size); + +void Curl_dnscache_destroy(struct Curl_dnscache *dns); + +/* prune old entries from the DNS cache */ +void Curl_dnscache_prune(struct Curl_easy *data); + +/* clear the DNS cache */ +void Curl_dnscache_clear(struct Curl_easy *data); + +/* + * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. + * + * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. + * + * The returned data *MUST* be "released" with Curl_dns_entry_unlink() after + * use, or we will leak memory! + * Returns CURLE_OK or CURLE_COULDNT_RESOLVE_HOST when a negative + * entry was in the cache. + */ +CURLcode Curl_dnscache_get(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + struct Curl_dns_entry **pentry); + +/* + * Curl_dnscache_addr() adds `entry` to the cache, increasing its + * reference count on success. + */ +CURLcode Curl_dnscache_add(struct Curl_easy *data, + struct Curl_dns_entry *entry); + +/* Store a "negative" entry for host:port, e.g. remember that + * it could not be resolved. */ +CURLcode Curl_dnscache_add_negative(struct Curl_easy *data, + uint8_t dns_queries, + const char *host, + uint16_t port); + +/* + * Populate the cache with specified entries from CURLOPT_RESOLVE. + */ +CURLcode Curl_loadhostpairs(struct Curl_easy *data); + +#endif /* HEADER_CURL_DNSCACHE_H */
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c index 4488d42..3044135 100644 --- a/Utilities/cmcurl/lib/doh.c +++ b/Utilities/cmcurl/lib/doh.c
@@ -40,6 +40,9 @@ #define DNS_CLASS_IN 0x01 +static void doh_close(struct Curl_easy *data, + struct Curl_resolv_async *async); + #ifdef CURLVERBOSE static const char * const errors[] = { "", @@ -71,6 +74,11 @@ */ UNITTEST DOHcode doh_req_encode(const char *host, DNStype dnstype, + unsigned char *dnsp, /* buffer */ + size_t len, /* buffer size */ + size_t *olen); /* output length */ +UNITTEST DOHcode doh_req_encode(const char *host, + DNStype dnstype, unsigned char *dnsp, /* buffer */ size_t len, /* buffer size */ size_t *olen) /* output length */ @@ -205,51 +213,62 @@ #endif /* called from multi when a sub transfer, e.g. doh probe, is done. - * This looks up the the probe response at its meta CURL_EZM_DOH_PROBE + * This looks up the probe response at its meta CURL_EZM_DOH_PROBE * and copies the response body over to the struct at the master's * meta at CURL_EZM_DOH_MASTER. */ static void doh_probe_done(struct Curl_easy *data, struct Curl_easy *doh, CURLcode result) { - struct doh_probes *dohp = data->state.async.doh; - DEBUGASSERT(dohp); - if(dohp) { - struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); - int i; + struct Curl_resolv_async *async = NULL; + struct doh_probes *dohp = NULL; + struct doh_request *doh_req = NULL; + int i; - for(i = 0; i < DOH_SLOT_COUNT; ++i) { - if(dohp->probe_resp[i].probe_mid == doh->mid) - break; - } - if(i >= DOH_SLOT_COUNT) { - failf(data, "unknown sub request done"); - return; - } + doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE); + if(!doh_req) { + DEBUGASSERT(0); + return; + } - dohp->pending--; - infof(doh, "a DoH request is completed, %u to go", dohp->pending); - dohp->probe_resp[i].result = result; - /* We expect either the meta data still to exist or the sub request - * to have already failed. */ - DEBUGASSERT(doh_req || result); - if(doh_req) { - if(!result) { - dohp->probe_resp[i].dnstype = doh_req->dnstype; - result = curlx_dyn_addn(&dohp->probe_resp[i].body, - curlx_dyn_ptr(&doh_req->resp_body), - curlx_dyn_len(&doh_req->resp_body)); - curlx_dyn_free(&doh_req->resp_body); - } - Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); - } + async = Curl_async_get(data, doh_req->resolv_id); + if(!async) { + CURL_TRC_DNS(data, "[%u] ignoring outdated DoH response", + doh_req->resolv_id); + return; + } + dohp = async->doh; - if(result) - infof(doh, "DoH request %s", curl_easy_strerror(result)); + for(i = 0; i < DOH_SLOT_COUNT; ++i) { + if(dohp->probe_resp[i].probe_mid == doh->mid) + break; + } + /* We really should have found the slot where to store the response */ + if(i >= DOH_SLOT_COUNT) { + DEBUGASSERT(0); + failf(data, "DoH: unknown sub request done"); + return; + } - if(!dohp->pending) { - /* DoH completed, run the transfer picking up the results */ - Curl_multi_mark_dirty(data); - } + dohp->pending--; + infof(doh, "a DoH request is completed, %u to go", dohp->pending); + dohp->probe_resp[i].result = result; + /* We expect either the meta data still to exist or the sub request + * to have already failed. */ + if(!result) { + dohp->probe_resp[i].dnstype = doh_req->dnstype; + result = curlx_dyn_addn(&dohp->probe_resp[i].body, + curlx_dyn_ptr(&doh_req->resp_body), + curlx_dyn_len(&doh_req->resp_body)); + curlx_dyn_free(&doh_req->resp_body); + } + Curl_meta_remove(doh, CURL_EZM_DOH_PROBE); + + if(result) + infof(doh, "DoH request %s", curl_easy_strerror(result)); + + if(!dohp->pending) { + /* DoH completed, run the transfer picking up the results */ + Curl_multi_mark_dirty(data); } } @@ -278,6 +297,7 @@ DNStype dnstype, const char *host, const char *url, CURLM *multi, + uint32_t resolv_id, uint32_t *pmid) { struct Curl_easy *doh = NULL; @@ -291,6 +311,7 @@ doh_req = curlx_calloc(1, sizeof(*doh_req)); if(!doh_req) return CURLE_OUT_OF_MEMORY; + doh_req->resolv_id = resolv_id; doh_req->dnstype = dnstype; curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE); @@ -431,29 +452,22 @@ * a 'Curl_addrinfo *' with the address information. */ -CURLcode Curl_doh(struct Curl_easy *data, const char *hostname, - int port, int ip_version) +CURLcode Curl_doh(struct Curl_easy *data, + struct Curl_resolv_async *async) { CURLcode result = CURLE_OK; struct doh_probes *dohp = NULL; - struct connectdata *conn = data->conn; size_t i; - DEBUGASSERT(conn); - DEBUGASSERT(!data->state.async.doh); - DEBUGASSERT(hostname && hostname[0]); - if(data->state.async.doh) - Curl_doh_cleanup(data); - - data->state.async.done = FALSE; - data->state.async.port = port; - data->state.async.ip_version = ip_version; - data->state.async.hostname = curlx_strdup(hostname); - if(!data->state.async.hostname) - return CURLE_OUT_OF_MEMORY; + DEBUGASSERT(!async->doh); + DEBUGASSERT(async->hostname[0]); + if(async->doh) { + DEBUGASSERT(0); /* should not happen */ + Curl_doh_cleanup(data, async); + } /* start clean, consider allocating this struct on demand */ - data->state.async.doh = dohp = curlx_calloc(1, sizeof(struct doh_probes)); + async->doh = dohp = curlx_calloc(1, sizeof(struct doh_probes)); if(!dohp) return CURLE_OUT_OF_MEMORY; @@ -462,28 +476,29 @@ curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE); } - conn->bits.doh = TRUE; - dohp->host = data->state.async.hostname; - dohp->port = data->state.async.port; + dohp->host = async->hostname; + dohp->port = async->port; /* We are making sub easy handles and want to be called back when * one is done. */ data->sub_xfer_done = doh_probe_done; /* create IPv4 DoH request */ - result = doh_probe_run(data, CURL_DNS_TYPE_A, - hostname, data->set.str[STRING_DOH], - data->multi, - &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); - if(result) - goto error; - dohp->pending++; + if(async->dns_queries & CURL_DNSQ_A) { + result = doh_probe_run(data, CURL_DNS_TYPE_A, + async->hostname, data->set.str[STRING_DOH], + data->multi, async->id, + &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid); + if(result) + goto error; + dohp->pending++; + } #ifdef USE_IPV6 - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) { + if(async->dns_queries & CURL_DNSQ_AAAA) { /* create IPv6 DoH request */ result = doh_probe_run(data, CURL_DNS_TYPE_AAAA, - hostname, data->set.str[STRING_DOH], - data->multi, + async->hostname, data->set.str[STRING_DOH], + data->multi, async->id, &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid); if(result) goto error; @@ -492,17 +507,17 @@ #endif #ifdef USE_HTTPSRR - if(conn->scheme->protocol & PROTO_FAMILY_HTTP) { - /* Only use HTTPS RR for HTTP(S) transfers */ + if(async->dns_queries & CURL_DNSQ_HTTPS) { char *qname = NULL; - if(port != PORT_HTTPS) { - qname = curl_maprintf("_%d._https.%s", port, hostname); + if(async->port != PORT_HTTPS) { + qname = curl_maprintf("_%d._https.%s", async->port, async->hostname); if(!qname) goto error; } result = doh_probe_run(data, CURL_DNS_TYPE_HTTPS, - qname ? qname : hostname, data->set.str[STRING_DOH], - data->multi, + qname ? qname : async->hostname, + data->set.str[STRING_DOH], data->multi, + async->id, &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid); curlx_free(qname); if(result) @@ -513,7 +528,7 @@ return CURLE_OK; error: - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); return result; } @@ -698,6 +713,8 @@ return DOH_OK; } +/* @unittest 1655 */ +UNITTEST void de_init(struct dohentry *de); UNITTEST void de_init(struct dohentry *de) { int i; @@ -707,6 +724,11 @@ curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME); } +/* @unittest 1655 */ +UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, + size_t dohlen, + DNStype dnstype, + struct dohentry *d); UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, size_t dohlen, DNStype dnstype, @@ -838,7 +860,7 @@ if(index != dohlen) return DOH_DNS_MALFORMAT; /* something is wrong */ -#ifdef USE_HTTTPS +#ifdef USE_HTTPSRR if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs) #else @@ -1024,6 +1046,8 @@ } #endif +/* @unittest 1655 */ +UNITTEST void de_cleanup(struct dohentry *d); UNITTEST void de_cleanup(struct dohentry *d) { int i = 0; @@ -1032,7 +1056,7 @@ } #ifdef USE_HTTPSRR for(i = 0; i < d->numhttps_rrs; i++) - Curl_safefree(d->https_rrs[i].val); + curlx_safefree(d->https_rrs[i].val); #endif } @@ -1049,7 +1073,7 @@ * https://datatracker.ietf.org/doc/html/rfc1035#section-3.1 * * The input buffer pointer will be modified so it points to after the end of - * the DNS name encoding on output. (And that is why it is an "unsigned char + * the DNS name encoding on output. (that is why it is an "unsigned char * **" :-) */ static CURLcode doh_decode_rdata_name(const unsigned char **buf, @@ -1095,11 +1119,10 @@ return CURLE_OK; } +/* @unittest 1658 */ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, const unsigned char *cp, size_t len, struct Curl_https_rrinfo **hrr); - -/* @unittest 1658 */ UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data, const unsigned char *cp, size_t len, struct Curl_https_rrinfo **hrr) @@ -1111,6 +1134,7 @@ CURLcode result = CURLE_OUT_OF_MEMORY; size_t olen; + (void)data; *hrr = NULL; if(len <= 2) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1128,7 +1152,6 @@ result = CURLE_WEIRD_SERVER_REPLY; goto err; } - lhrr->port = -1; /* until set */ while(len >= 4) { pcode = doh_get16bit(cp, 0); plen = doh_get16bit(cp, 2); @@ -1138,9 +1161,10 @@ result = CURLE_WEIRD_SERVER_REPLY; goto err; } - result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen); + result = Curl_httpsrr_set(lhrr, pcode, cp, plen); if(result) goto err; + Curl_httpsrr_trace(data, lhrr); cp += plen; len -= plen; expected_min_pcode = pcode + 1; @@ -1150,16 +1174,13 @@ return CURLE_OK; err: Curl_httpsrr_cleanup(lhrr); - Curl_safefree(lhrr); + curlx_safefree(lhrr); return result; } #if defined(DEBUGBUILD) && defined(CURLVERBOSE) -UNITTEST void doh_print_httpsrr(struct Curl_easy *data, - struct Curl_https_rrinfo *hrr); - -UNITTEST void doh_print_httpsrr(struct Curl_easy *data, - struct Curl_https_rrinfo *hrr) +static void doh_print_httpsrr(struct Curl_easy *data, + struct Curl_https_rrinfo *hrr) { DEBUGASSERT(hrr); infof(data, "HTTPS RR: priority %d, target: %s", hrr->priority, hrr->target); @@ -1194,32 +1215,31 @@ # endif #endif -CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dnsp) +CURLcode Curl_doh_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) { + struct doh_probes *dohp = async->doh; CURLcode result = CURLE_OK; - struct doh_probes *dohp = data->state.async.doh; struct dohentry de; - *dnsp = NULL; /* defaults to no response */ + + *pdns = NULL; /* defaults to no response */ if(!dohp) return CURLE_OUT_OF_MEMORY; if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT32_MAX && dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT32_MAX) { failf(data, "Could not DoH-resolve: %s", dohp->host); - return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY : - CURLE_COULDNT_RESOLVE_HOST; + return async->for_proxy ? + CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; } else if(!dohp->pending) { DOHcode rc[DOH_SLOT_COUNT]; int slot; - /* Clear any result the might still be there */ - Curl_resolv_unlink(data, &data->state.async.dns); - memset(rc, 0, sizeof(rc)); /* remove DoH handles from multi handle and close them */ - Curl_doh_close(data); + doh_close(data, async); /* parse the responses, create the struct and return it! */ de_init(&de); for(slot = 0; slot < DOH_SLOT_COUNT; slot++) { @@ -1235,7 +1255,6 @@ } } /* next slot */ - result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */ if(!rc[DOH_SLOT_IPV4] || !rc[DOH_SLOT_IPV6]) { /* we have an address, of one kind or other */ struct Curl_dns_entry *dns; @@ -1251,50 +1270,56 @@ goto error; /* we got a response, create a dns entry. */ - dns = Curl_dnscache_mk_entry(data, &ai, dohp->host, 0, - dohp->port, FALSE); - if(dns) { - /* Now add and HTTPSRR information if we have */ -#ifdef USE_HTTPSRR - if(de.numhttps_rrs > 0 && result == CURLE_OK) { - struct Curl_https_rrinfo *hrr = NULL; - result = doh_resp_decode_httpsrr(data, de.https_rrs->val, - de.https_rrs->len, &hrr); - if(result) { - infof(data, "Failed to decode HTTPS RR"); - Curl_resolv_unlink(data, &dns); - goto error; - } - infof(data, "Some HTTPS RR to process"); -#if defined(DEBUGBUILD) && defined(CURLVERBOSE) - doh_print_httpsrr(data, hrr); -#endif - dns->hinfo = hrr; - } -#endif /* USE_HTTPSRR */ - /* and add the entry to the cache */ - data->state.async.dns = dns; - result = Curl_dnscache_add(data, dns); - *dnsp = data->state.async.dns; + dns = Curl_dnscache_mk_entry(data, async->dns_queries, + &ai, dohp->host, dohp->port); + if(!dns) { + result = CURLE_OUT_OF_MEMORY; + goto error; } - } /* address processing done */ - /* All done */ - data->state.async.done = TRUE; + /* Now add and HTTPSRR information if we have */ +#ifdef USE_HTTPSRR + if(de.numhttps_rrs > 0 && result == CURLE_OK) { + struct Curl_https_rrinfo *hrr = NULL; + result = doh_resp_decode_httpsrr(data, de.https_rrs->val, + de.https_rrs->len, &hrr); + if(result) { + infof(data, "Failed to decode HTTPS RR"); + Curl_dns_entry_unlink(data, &dns); + goto error; + } + infof(data, "Some HTTPS RR to process"); +#if defined(DEBUGBUILD) && defined(CURLVERBOSE) + doh_print_httpsrr(data, hrr); +#endif + Curl_dns_entry_set_https_rr(dns, hrr); + } +#endif /* USE_HTTPSRR */ + + /* and add the entry to the cache */ + result = Curl_dnscache_add(data, dns); + *pdns = dns; + } /* address processing done */ + else { + result = async->for_proxy ? + CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST; + } + } /* !dohp->pending */ else /* wait for pending DoH transactions to complete */ - return CURLE_OK; + return CURLE_AGAIN; error: de_cleanup(&de); - Curl_doh_cleanup(data); + Curl_doh_cleanup(data, async); return result; } -void Curl_doh_close(struct Curl_easy *data) +static void doh_close(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct doh_probes *doh = data->state.async.doh; + struct doh_probes *doh = async ? async->doh : NULL; if(doh && data->multi) { struct Curl_easy *probe_data; uint32_t mid; @@ -1320,16 +1345,17 @@ } } -void Curl_doh_cleanup(struct Curl_easy *data) +void Curl_doh_cleanup(struct Curl_easy *data, + struct Curl_resolv_async *async) { - struct doh_probes *dohp = data->state.async.doh; + struct doh_probes *dohp = async->doh; if(dohp) { int i; - Curl_doh_close(data); + doh_close(data, async); for(i = 0; i < DOH_SLOT_COUNT; ++i) { curlx_dyn_free(&dohp->probe_resp[i].body); } - Curl_safefree(data->state.async.doh); + curlx_safefree(async->doh); } }
diff --git a/Utilities/cmcurl/lib/doh.h b/Utilities/cmcurl/lib/doh.h index 3eba4df..428b230 100644 --- a/Utilities/cmcurl/lib/doh.h +++ b/Utilities/cmcurl/lib/doh.h
@@ -25,7 +25,10 @@ ***************************************************************************/ #include "urldata.h" -#ifndef CURL_DISABLE_DOH +/* enums outside of the #ifdef to make the types work in unitprotos.h even on + builds without DoH support */ + +struct Curl_resolv_async; typedef enum { DOH_OK, @@ -53,6 +56,10 @@ CURL_DNS_TYPE_HTTPS = 65 } DNStype; +struct dohentry; /* forward-declare for non-DoH builds */ + +#ifndef CURL_DISABLE_DOH + enum doh_slot_num { /* Explicit values for first two symbols so as to match hard-coded * constants in existing code @@ -86,6 +93,7 @@ struct curl_slist *req_hds; struct dynbuf resp_body; size_t req_body_len; + uint32_t resolv_id; /* id of the resolve operation */ DNStype dnstype; }; @@ -101,7 +109,7 @@ struct doh_probes { struct doh_response probe_resp[DOH_SLOT_COUNT]; unsigned int pending; /* still outstanding probes */ - int port; + uint16_t port; const char *host; }; @@ -109,12 +117,12 @@ * Curl_doh() starts a name resolve using DoH (DNS-over-HTTPS). It resolves a * name and returns a 'Curl_addrinfo *' with the address information. */ +CURLcode Curl_doh(struct Curl_easy *data, + struct Curl_resolv_async *async); -CURLcode Curl_doh(struct Curl_easy *data, const char *hostname, - int port, int ip_version); - -CURLcode Curl_doh_is_resolved(struct Curl_easy *data, - struct Curl_dns_entry **dnsp); +CURLcode Curl_doh_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns); #define DOH_MAX_ADDR 24 #define DOH_MAX_CNAME 4 @@ -155,27 +163,15 @@ #endif }; -void Curl_doh_close(struct Curl_easy *data); -void Curl_doh_cleanup(struct Curl_easy *data); +void Curl_doh_cleanup(struct Curl_easy *data, + struct Curl_resolv_async *async); +#define Curl_doh_wanted(d) (!!(d)->set.doh) -#ifdef UNITTESTS -UNITTEST DOHcode doh_req_encode(const char *host, - DNStype dnstype, - unsigned char *dnsp, /* buffer */ - size_t len, /* buffer size */ - size_t *olen); /* output length */ -UNITTEST DOHcode doh_resp_decode(const unsigned char *doh, - size_t dohlen, - DNStype dnstype, - struct dohentry *d); - -UNITTEST void de_init(struct dohentry *d); -UNITTEST void de_cleanup(struct dohentry *d); -#endif #else /* CURL_DISABLE_DOH */ -#define Curl_doh(a, b, c, d, e) NULL -#define Curl_doh_is_resolved(x, y) CURLE_COULDNT_RESOLVE_HOST +#define Curl_doh(a, b) NULL +#define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST +#define Curl_doh_wanted(d) FALSE #endif /* !CURL_DISABLE_DOH */ #endif /* HEADER_CURL_DOH_H */
diff --git a/Utilities/cmcurl/lib/dynhds.c b/Utilities/cmcurl/lib/dynhds.c index 7424a57..097306c 100644 --- a/Utilities/cmcurl/lib/dynhds.c +++ b/Utilities/cmcurl/lib/dynhds.c
@@ -76,7 +76,7 @@ entry_free(dynhds->hds[i]); } } - Curl_safefree(dynhds->hds); + curlx_safefree(dynhds->hds); dynhds->hds_len = dynhds->hds_allc = dynhds->strs_len = 0; } @@ -158,7 +158,7 @@ if(dynhds->hds) { memcpy(nhds, dynhds->hds, dynhds->hds_len * sizeof(struct dynhds_entry *)); - Curl_safefree(dynhds->hds); + curlx_safefree(dynhds->hds); } dynhds->hds = nhds; dynhds->hds_allc = nallc; @@ -220,21 +220,36 @@ } #ifdef UNITTESTS -/* used by unit2602.c */ +/* @unittest 2602 */ -bool Curl_dynhds_contains(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Return TRUE iff one or more headers with the given name exist. + */ +UNITTEST bool dynhds_contains(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST bool dynhds_contains(struct dynhds *dynhds, + const char *name, size_t namelen) { return !!Curl_dynhds_get(dynhds, name, namelen); } -bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name) +/* @unittest 2602 */ +UNITTEST bool dynhds_ccontains(struct dynhds *dynhds, const char *name); +UNITTEST bool dynhds_ccontains(struct dynhds *dynhds, const char *name) { - return Curl_dynhds_contains(dynhds, name, strlen(name)); + return dynhds_contains(dynhds, name, strlen(name)); } -size_t Curl_dynhds_count_name(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Return how often the given name appears in `dynhds`. + * Names are case-insensitive. + * + * @unittest 2602 + */ +UNITTEST size_t dynhds_count_name(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST size_t dynhds_count_name(struct dynhds *dynhds, + const char *name, size_t namelen) { size_t n = 0; if(dynhds->hds_len) { @@ -248,21 +263,30 @@ return n; } -size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name) +/** + * Return how often the given null-terminated name appears in `dynhds`. + * Names are case-insensitive. + * + * @unittest 2602 + */ +UNITTEST size_t dynhds_ccount_name(struct dynhds *dynhds, + const char *name); +UNITTEST size_t dynhds_ccount_name(struct dynhds *dynhds, + const char *name) { - return Curl_dynhds_count_name(dynhds, name, strlen(name)); + return dynhds_count_name(dynhds, name, strlen(name)); } -CURLcode Curl_dynhds_set(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen) -{ - Curl_dynhds_remove(dynhds, name, namelen); - return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); -} - -size_t Curl_dynhds_remove(struct dynhds *dynhds, - const char *name, size_t namelen) +/** + * Remove all entries with the given name. + * Returns number of entries removed. + * + * @unittest 2602 + */ +UNITTEST size_t dynhds_remove(struct dynhds *dynhds, + const char *name, size_t namelen); +UNITTEST size_t dynhds_remove(struct dynhds *dynhds, + const char *name, size_t namelen) { size_t n = 0; if(dynhds->hds_len) { @@ -287,12 +311,32 @@ return n; } -size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name) +/** + * Set the give header name and value, replacing any entries with + * the same name. The header is added at the end of all (remaining) + * entries. + * + * @unittest 2602 + */ +UNITTEST CURLcode dynhds_set(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen); +UNITTEST CURLcode dynhds_set(struct dynhds *dynhds, + const char *name, size_t namelen, + const char *value, size_t valuelen) { - return Curl_dynhds_remove(dynhds, name, strlen(name)); + dynhds_remove(dynhds, name, namelen); + return Curl_dynhds_add(dynhds, name, namelen, value, valuelen); } -#endif +/* @unittest 2602 */ +UNITTEST size_t dynhds_cremove(struct dynhds *dynhds, const char *name); +UNITTEST size_t dynhds_cremove(struct dynhds *dynhds, const char *name) +{ + return dynhds_remove(dynhds, name, strlen(name)); +} + +#endif /* UNITTESTS */ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf) {
diff --git a/Utilities/cmcurl/lib/dynhds.h b/Utilities/cmcurl/lib/dynhds.h index e30eb45..d5337eb 100644 --- a/Utilities/cmcurl/lib/dynhds.h +++ b/Utilities/cmcurl/lib/dynhds.h
@@ -94,47 +94,6 @@ const char *name, size_t namelen); struct dynhds_entry *Curl_dynhds_cget(struct dynhds *dynhds, const char *name); -#ifdef UNITTESTS -/* used by unit2602.c */ - -/** - * Return TRUE iff one or more headers with the given name exist. - */ -bool Curl_dynhds_contains(struct dynhds *dynhds, - const char *name, size_t namelen); -bool Curl_dynhds_ccontains(struct dynhds *dynhds, const char *name); - -/** - * Return how often the given name appears in `dynhds`. - * Names are case-insensitive. - */ -size_t Curl_dynhds_count_name(struct dynhds *dynhds, - const char *name, size_t namelen); - -/** - * Return how often the given null-terminated name appears in `dynhds`. - * Names are case-insensitive. - */ -size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name); - -/** - * Remove all entries with the given name. - * Returns number of entries removed. - */ -size_t Curl_dynhds_remove(struct dynhds *dynhds, - const char *name, size_t namelen); -size_t Curl_dynhds_cremove(struct dynhds *dynhds, const char *name); - -/** - * Set the give header name and value, replacing any entries with - * the same name. The header is added at the end of all (remaining) - * entries. - */ -CURLcode Curl_dynhds_set(struct dynhds *dynhds, - const char *name, size_t namelen, - const char *value, size_t valuelen); -#endif - CURLcode Curl_dynhds_cset(struct dynhds *dynhds, const char *name, const char *value); @@ -167,7 +126,7 @@ /** * Add the headers to the given `dynbuf` in HTTP/1.1 format with - * cr+lf line endings. Will NOT output a last empty line. + * CR+LF line endings. Does NOT output a last empty line. */ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf);
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c index d05674e..a472d6d 100644 --- a/Utilities/cmcurl/lib/easy.c +++ b/Utilities/cmcurl/lib/easy.c
@@ -779,8 +779,9 @@ if(multi->in_callback) return CURLE_RECURSIVE_API_CALL; - /* Copy the MAXCONNECTS option to the multi handle */ + /* Copy relevant easy options to the multi handle */ curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects); + curl_multi_setopt(multi, CURLMOPT_QUICK_EXIT, (long)data->set.quick_exit); data->multi_easy = NULL; /* pretend it does not exist */ mresult = curl_multi_add_handle(multi, data); @@ -814,9 +815,9 @@ * curl_easy_perform() is the external interface that performs a blocking * transfer as previously setup. */ -CURLcode curl_easy_perform(CURL *data) +CURLcode curl_easy_perform(CURL *curl) { - return easy_perform(data, FALSE); + return easy_perform(curl, FALSE); } #ifdef DEBUGBUILD @@ -824,9 +825,9 @@ * curl_easy_perform_ev() is the external interface that performs a blocking * transfer using the event-based API internally. */ -CURLcode curl_easy_perform_ev(struct Curl_easy *data) +CURLcode curl_easy_perform_ev(struct Curl_easy *easy) { - return easy_perform(data, TRUE); + return easy_perform(easy, TRUE); } #endif @@ -834,9 +835,9 @@ * curl_easy_cleanup() is the external interface to cleaning/freeing the given * easy handle. */ -void curl_easy_cleanup(CURL *ptr) +void curl_easy_cleanup(CURL *curl) { - struct Curl_easy *data = ptr; + struct Curl_easy *data = curl; if(GOOD_EASY_HANDLE(data)) { struct Curl_sigpipe_ctx sigpipe_ctx; sigpipe_ignore(data, &sigpipe_ctx); @@ -850,9 +851,9 @@ * information from a performed transfer and similar. */ #undef curl_easy_getinfo -CURLcode curl_easy_getinfo(CURL *easy, CURLINFO info, ...) +CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; va_list arg; void *paramp; CURLcode result; @@ -938,7 +939,7 @@ static void dupeasy_meta_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -949,9 +950,9 @@ * given input easy handle. The returned handle will be a new working handle * with all options set exactly as the input source handle. */ -CURL *curl_easy_duphandle(CURL *d) +CURL *curl_easy_duphandle(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; struct Curl_easy *outcurl = NULL; if(!GOOD_EASY_HANDLE(data)) @@ -1080,9 +1081,9 @@ * curl_easy_reset() is an external interface that allows an app to re- * initialize a session handle to the default values. */ -void curl_easy_reset(CURL *d) +void curl_easy_reset(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return; @@ -1091,10 +1092,6 @@ /* clear all meta data */ Curl_meta_reset(data); - /* clear any resolve data */ - Curl_async_shutdown(data); - Curl_resolv_unlink(data, &data->state.dns[0]); - Curl_resolv_unlink(data, &data->state.dns[1]); /* zero out UserDefined data: */ Curl_freeset(data); memset(&data->set, 0, sizeof(struct UserDefined)); @@ -1133,12 +1130,12 @@ * NOTE: This is one of few API functions that are allowed to be called from * within a callback. */ -CURLcode curl_easy_pause(CURL *d, int action) +CURLcode curl_easy_pause(CURL *curl, int action) { CURLcode result = CURLE_OK; bool recursive = FALSE; bool changed = FALSE; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; bool recv_paused, recv_paused_new; bool send_paused, send_paused_new; @@ -1157,12 +1154,14 @@ if((send_paused != send_paused_new) || (send_paused_new != Curl_creader_is_paused(data))) { changed = TRUE; - result = Curl_1st_err(result, Curl_xfer_pause_send(data, send_paused_new)); + result = Curl_1st_fatal( + result, Curl_xfer_pause_send(data, send_paused_new)); } if(recv_paused != recv_paused_new) { changed = TRUE; - result = Curl_1st_err(result, Curl_xfer_pause_recv(data, recv_paused_new)); + result = Curl_1st_fatal( + result, Curl_xfer_pause_recv(data, recv_paused_new)); } /* If not completely pausing both directions now, run again in any case. */ @@ -1221,11 +1220,11 @@ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. * Returns CURLE_OK on success, error code on error. */ -CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n) +CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) { CURLcode result; struct connectdata *c; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1299,11 +1298,12 @@ * Sends data over the connected socket. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ -CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n) +CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, + size_t *n) { size_t written = 0; CURLcode result; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; if(Curl_is_in_callback(data)) @@ -1317,9 +1317,9 @@ /* * Performs connection upkeep for the given session handle. */ -CURLcode curl_easy_upkeep(CURL *d) +CURLcode curl_easy_upkeep(CURL *curl) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; /* Verify that we got an easy handle we can work with. */ if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1331,18 +1331,20 @@ return Curl_cpool_upkeep(data); } -CURLcode curl_easy_ssls_import(CURL *d, const char *session_key, +CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, const unsigned char *shmac, size_t shmac_len, const unsigned char *sdata, size_t sdata_len) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data)) + return CURLE_RECURSIVE_API_CALL; return Curl_ssl_session_import(data, session_key, shmac, shmac_len, sdata, sdata_len); #else - (void)d; + (void)curl; (void)session_key; (void)shmac; (void)shmac_len; @@ -1352,17 +1354,19 @@ #endif } -CURLcode curl_easy_ssls_export(CURL *d, +CURLcode curl_easy_ssls_export(CURL *curl, curl_ssls_export_cb *export_fn, void *userptr) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data)) + return CURLE_RECURSIVE_API_CALL; return Curl_ssl_session_export(data, export_fn, userptr); #else - (void)d; + (void)curl; (void)export_fn; (void)userptr; return CURLE_NOT_BUILT_IN;
diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h index cefb01d..b8f916f 100644 --- a/Utilities/cmcurl/lib/easy_lock.h +++ b/Utilities/cmcurl/lib/easy_lock.h
@@ -49,8 +49,8 @@ __has_builtin() function, so override it. */ /* if GCC on i386/x86_64 or if the built-in is present */ -#if (defined(__GNUC__) && !defined(__clang__)) && \ - (defined(__i386__) || defined(__x86_64__)) +#if (defined(__GNUC__) && !defined(__clang__)) && \ + (defined(__i386__) || defined(__x86_64__)) #define HAVE_BUILTIN_IA32_PAUSE #elif defined(__has_builtin) /* Keep this PP check separate from others */ #if __has_builtin(__builtin_ia32_pause) @@ -84,9 +84,7 @@ atomic_store_explicit(lock, false, memory_order_release); } -#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H) - -#include <pthread.h> +#elif defined(HAVE_THREADS_POSIX) #define curl_simple_lock pthread_mutex_t #define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER
diff --git a/Utilities/cmcurl/lib/easygetopt.c b/Utilities/cmcurl/lib/easygetopt.c index 2685ada..533d7e3 100644 --- a/Utilities/cmcurl/lib/easygetopt.c +++ b/Utilities/cmcurl/lib/easygetopt.c
@@ -62,8 +62,8 @@ } /* Iterates over available options */ -const struct curl_easyoption * -curl_easy_option_next(const struct curl_easyoption *prev) +const struct curl_easyoption *curl_easy_option_next( + const struct curl_easyoption *prev) { if(prev && prev->name) { prev++; @@ -88,8 +88,8 @@ return NULL; } -const struct curl_easyoption * -curl_easy_option_next(const struct curl_easyoption *prev) +const struct curl_easyoption *curl_easy_option_next( + const struct curl_easyoption *prev) { (void)prev; return NULL;
diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c index be44e97..4aff583 100644 --- a/Utilities/cmcurl/lib/escape.c +++ b/Utilities/cmcurl/lib/escape.c
@@ -33,9 +33,9 @@ #include "curl_printf.h" /* for ABI-compatibility with previous versions */ -char *curl_escape(const char *string, int inlength) +char *curl_escape(const char *string, int length) { - return curl_easy_escape(NULL, string, inlength); + return curl_easy_escape(NULL, string, length); } /* for ABI-compatibility with previous versions */ @@ -47,25 +47,25 @@ /* Escapes for URL the given unescaped string of given length. * 'data' is ignored since 7.82.0. */ -char *curl_easy_escape(CURL *data, const char *string, int inlength) +char *curl_easy_escape(CURL *curl, const char *string, int length) { - size_t length; + size_t len; struct dynbuf d; - (void)data; + (void)curl; - if(!string || (inlength < 0)) + if(!string || (length < 0)) return NULL; - length = (inlength ? (size_t)inlength : strlen(string)); - if(!length) + len = (length ? (size_t)length : strlen(string)); + if(!len) return curlx_strdup(""); - if(length > SIZE_MAX / 16) + if(len > SIZE_MAX / 16) return NULL; - curlx_dyn_init(&d, (length * 3) + 1); + curlx_dyn_init(&d, (len * 3) + 1); - while(length--) { + while(len--) { /* treat the characters unsigned */ unsigned char in = (unsigned char)*string++; @@ -138,7 +138,7 @@ if(((ctrl == REJECT_CTRL) && (in < 0x20)) || ((ctrl == REJECT_ZERO) && (in == 0))) { - Curl_safefree(*ostring); + curlx_safefree(*ostring); return CURLE_URL_MALFORMAT; } @@ -160,24 +160,25 @@ * If olen == NULL, no output length is stored. * 'data' is ignored since 7.82.0. */ -char *curl_easy_unescape(CURL *data, const char *string, int length, int *olen) +char *curl_easy_unescape(CURL *curl, const char *string, int inlength, + int *outlength) { char *str = NULL; - (void)data; - if(string && (length >= 0)) { - size_t inputlen = (size_t)length; + (void)curl; + if(string && (inlength >= 0)) { + size_t inputlen = (size_t)inlength; size_t outputlen; CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, REJECT_NADA); if(res) return NULL; - if(olen) { + if(outlength) { if(outputlen <= (size_t)INT_MAX) - *olen = curlx_uztosi(outputlen); + *outlength = curlx_uztosi(outputlen); else /* too large to return in an int, fail! */ - Curl_safefree(str); + curlx_safefree(str); } } return str;
diff --git a/Utilities/cmcurl/lib/fake_addrinfo.c b/Utilities/cmcurl/lib/fake_addrinfo.c index d8b1398..5a89202 100644 --- a/Utilities/cmcurl/lib/fake_addrinfo.c +++ b/Utilities/cmcurl/lib/fake_addrinfo.c
@@ -41,16 +41,16 @@ } struct context { - struct ares_addrinfo *result; + struct ares_addrinfo *addr; }; static void async_addrinfo_cb(void *userp, int status, int timeouts, - struct ares_addrinfo *result) + struct ares_addrinfo *addr) { struct context *ctx = (struct context *)userp; (void)timeouts; if(ARES_SUCCESS == status) { - ctx->result = result; + ctx->addr = addr; } } @@ -184,11 +184,11 @@ /* Wait until no more requests are left to be processed */ ares_queue_wait_empty(channel, -1); - if(ctx.result) { + if(ctx.addr) { /* convert the c-ares version */ - *res = mk_getaddrinfo(ctx.result); + *res = mk_getaddrinfo(ctx.addr); /* free the old */ - ares_freeaddrinfo(ctx.result); + ares_freeaddrinfo(ctx.addr); } else rc = EAI_NONAME; /* got nothing */
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c index 1c3f663..fff8fee 100644 --- a/Utilities/cmcurl/lib/file.c +++ b/Utilities/cmcurl/lib/file.c
@@ -84,7 +84,7 @@ static void file_cleanup(struct FILEPROTO *file) { - Curl_safefree(file->freepath); + curlx_safefree(file->freepath); file->path = NULL; if(file->fd != -1) { curlx_close(file->fd); @@ -108,6 +108,8 @@ (void)conn; /* allocate the FILE specific struct */ filep = curlx_calloc(1, sizeof(*filep)); + if(filep) + filep->fd = -1; if(!filep || Curl_meta_set(data, CURL_META_FILE_EASY, filep, file_easy_dtor)) return CURLE_OUT_OF_MEMORY; @@ -191,7 +193,7 @@ if(actual_path[i] == '/') actual_path[i] = '\\'; else if(!actual_path[i]) { /* binary zero */ - Curl_safefree(real_path); + curlx_safefree(real_path); return CURLE_URL_MALFORMAT; } @@ -200,7 +202,7 @@ #else if(memchr(real_path, 0, real_path_len)) { /* binary zeroes indicate foul play */ - Curl_safefree(real_path); + curlx_safefree(real_path); return CURLE_URL_MALFORMAT; } @@ -598,7 +600,7 @@ return result; } -static const struct Curl_protocol Curl_protocol_file = { +const struct Curl_protocol Curl_protocol_file = { file_setup_connection, /* setup_connection */ file_do, /* do_it */ file_done, /* done */ @@ -613,25 +615,9 @@ file_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif - -/* - * FILE scheme handler. - */ -const struct Curl_scheme Curl_scheme_file = { - "file", /* scheme */ -#ifdef CURL_DISABLE_FILE - ZERO_NULL, -#else - &Curl_protocol_file, -#endif - CURLPROTO_FILE, /* protocol */ - CURLPROTO_FILE, /* family */ - PROTOPT_NONETWORK | PROTOPT_NOURLQUERY, /* flags */ - 0 /* defport */ -};
diff --git a/Utilities/cmcurl/lib/file.h b/Utilities/cmcurl/lib/file.h index a306544..0aa411b 100644 --- a/Utilities/cmcurl/lib/file.h +++ b/Utilities/cmcurl/lib/file.h
@@ -23,6 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_file; +#ifndef CURL_DISABLE_FILE +extern const struct Curl_protocol Curl_protocol_file; +#endif #endif /* HEADER_CURL_FILE_H */
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c index 3770d28..3619c15 100644 --- a/Utilities/cmcurl/lib/formdata.c +++ b/Utilities/cmcurl/lib/formdata.c
@@ -609,11 +609,11 @@ struct curl_httppost **last_post, ...) { va_list arg; - CURLFORMcode result; + CURLFORMcode form; va_start(arg, last_post); - result = FormAdd(httppost, last_post, arg); + form = FormAdd(httppost, last_post, arg); va_end(arg); - return result; + return form; } /* @@ -691,16 +691,16 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len) { char *zname; - CURLcode res; + CURLcode result; if(!name || !len) return curl_mime_name(part, name); zname = curlx_memdup0(name, len); if(!zname) return CURLE_OUT_OF_MEMORY; - res = curl_mime_name(part, zname); + result = curl_mime_name(part, zname); curlx_free(zname); - return res; + return result; } /*
diff --git a/Utilities/cmcurl/lib/ftp-int.h b/Utilities/cmcurl/lib/ftp-int.h new file mode 100644 index 0000000..68d26f3 --- /dev/null +++ b/Utilities/cmcurl/lib/ftp-int.h
@@ -0,0 +1,160 @@ +#ifndef HEADER_CURL_FTP_INT_H +#define HEADER_CURL_FTP_INT_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "pingpong.h" + +#ifndef CURL_DISABLE_FTP + +/**************************************************************************** + * FTP unique setup + ***************************************************************************/ +enum { + FTP_STOP, /* do nothing state, stops the state machine */ + FTP_WAIT220, /* waiting for the initial 220 response immediately after + a connect */ + FTP_AUTH, + FTP_USER, + FTP_PASS, + FTP_ACCT, + FTP_PBSZ, + FTP_PROT, + FTP_CCC, + FTP_PWD, + FTP_SYST, + FTP_NAMEFMT, + FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ + FTP_RETR_PREQUOTE, + FTP_STOR_PREQUOTE, + FTP_LIST_PREQUOTE, + FTP_POSTQUOTE, + FTP_CWD, /* change directory */ + FTP_MKD, /* if the directory did not exist */ + FTP_MDTM, /* to figure out the datestamp */ + FTP_TYPE, /* to set type when doing a head-like request */ + FTP_LIST_TYPE, /* set type when about to do a directory list */ + FTP_RETR_LIST_TYPE, + FTP_RETR_TYPE, /* set type when about to RETR a file */ + FTP_STOR_TYPE, /* set type when about to STOR a file */ + FTP_SIZE, /* get the remote file's size for head-like request */ + FTP_RETR_SIZE, /* get the remote file's size for RETR */ + FTP_STOR_SIZE, /* get the size for STOR */ + FTP_REST, /* when used to check if the server supports it in head-like */ + FTP_RETR_REST, /* when asking for "resume" in for RETR */ + FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ + FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ + FTP_PASV, /* generic state for PASV and EPSV, check count1 */ + FTP_LIST, /* generic state for LIST, NLST or a custom list command */ + FTP_RETR, + FTP_STOR, /* generic state for STOR and APPE */ + FTP_QUIT, + FTP_LAST /* never used */ +}; +typedef unsigned char ftpstate; /* use the enum values */ + +struct ftp_parselist_data; /* defined later in ftplistparser.c */ + +struct ftp_wc { + struct ftp_parselist_data *parser; + + struct { + curl_write_callback write_function; + FILE *file_descriptor; + } backup; +}; + +/* This FTP struct is used in the Curl_easy. All FTP data that is + connection-oriented must be in FTP_conn to properly deal with the fact that + perhaps the Curl_easy is changed between the times the connection is + used. */ +struct FTP { + char *path; /* points to the urlpieces struct field */ + char *pathalloc; /* if non-NULL a pointer to an allocated path */ + + /* transfer a file/body or not, done as a typedefed enum to make debuggers + display the full symbol and not the numerical value */ + curl_pp_transfer transfer; + curl_off_t downloadsize; +}; + +/* one struct entry for each path component (of 'rawpath') */ +struct pathcomp { + int start; /* start column */ + int len; /* length in bytes */ +}; + +/* ftp_conn is used for struct connection-oriented data in the connectdata + struct */ +struct ftp_conn { + struct pingpong pp; + char *account; + char *alternative_to_user; + char *entrypath; /* the PWD reply when we logged on */ + const char *file; /* url-decoded filename (or path), points into rawpath */ + char *rawpath; /* URL decoded, allocated, version of the path */ + struct pathcomp *dirs; /* allocated array for path components */ + char *prevpath; /* url-decoded conn->path from the previous transfer */ + char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a + and others (A/I or zero) */ + char *server_os; /* The target server operating system. */ + curl_off_t known_filesize; /* file size is different from -1, if wildcard + LIST parsing was done and wc_statemach set + it */ + int count1; /* general purpose counter for the state machine */ + int count2; /* general purpose counter for the state machine */ + int count3; /* general purpose counter for the state machine */ + unsigned short dirdepth; /* number of entries used in the 'dirs' array, + < FTP_MAX_DIR_DEPTH */ + unsigned short cwdcount; /* number of CWD commands issued, + < FTP_MAX_DIR_DEPTH */ + unsigned char state; /* (ftpstate enum) always use ftp.c:state() to change + state! */ + unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or + IMAP or POP3 or others! (type: curl_usessl)*/ + unsigned char ccc; /* ccc level for this connection */ + BIT(ftp_trying_alternative); + BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) + file size and 226/250 status check. It should still + read the line, ignore the result. */ + BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If + the connection has timed out or been closed, this + should be FALSE when it gets to Curl_ftp_quit() */ + BIT(cwddone); /* if it has been determined that the proper CWD combo + already has been done */ + BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent + caching the current directory */ + BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ + BIT(shutdown); /* connection is being shutdown, e.g. QUIT */ +}; + +/* meta key for storing `struct FTP` as easy meta data */ +#define CURL_META_FTP_EASY "meta:proto:ftp:easy" +/* meta key for storing `struct ftp_conn` as connection meta data */ +#define CURL_META_FTP_CONN "meta:proto:ftp:conn" + +#endif /* CURL_DISABLE_FTP */ + +#endif /* HEADER_CURL_FTP_INT_H */
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c index 5b1151e..61a78c2 100644 --- a/Utilities/cmcurl/lib/ftp.c +++ b/Utilities/cmcurl/lib/ftp.c
@@ -49,6 +49,7 @@ #include "transfer.h" #include "escape.h" #include "ftp.h" +#include "ftp-int.h" #include "ftplistparser.h" #include "curl_range.h" #include "strcase.h" @@ -67,6 +68,7 @@ #include "curlx/strdup.h" #include "curlx/strerr.h" #include "curlx/strparse.h" +#include "curl_ctype.h" #ifndef NI_MAXHOST #define NI_MAXHOST 1025 @@ -173,9 +175,9 @@ static void freedirs(struct ftp_conn *ftpc) { - Curl_safefree(ftpc->dirs); + curlx_safefree(ftpc->dirs); ftpc->dirdepth = 0; - Curl_safefree(ftpc->rawpath); + curlx_safefree(ftpc->rawpath); ftpc->file = NULL; } @@ -209,7 +211,7 @@ const char *slashPos = NULL; const char *fileName = NULL; CURLcode result = CURLE_OK; - const char *rawPath = NULL; /* url-decoded "raw" path */ + const char *rawPath = NULL; /* URL-decoded "raw" path */ size_t pathLen = 0; ftpc->ctl_valid = FALSE; @@ -217,7 +219,7 @@ if(ftpc->rawpath) freedirs(ftpc); - /* url-decode ftp path before further evaluation */ + /* URL-decode ftp path before further evaluation */ result = Curl_urldecode(ftp->path, 0, &ftpc->rawpath, &pathLen, REJECT_CTRL); if(result) { failf(data, "path contains control characters"); @@ -232,8 +234,8 @@ fileName = rawPath; /* this is a full file path */ /* else: ftpc->file is not used anywhere other than for operations on - a file. In other words, never for directory operations. - So we can safely leave filename as NULL here and use it as a + a file. In other words, never for directory operations, + so we can safely leave filename as NULL here and use it as a argument in dir/file decisions. */ break; @@ -305,7 +307,7 @@ ftpc->file = fileName; else ftpc->file = NULL; /* instead of point to a zero byte, - we make it a NULL pointer */ + we make it a NULL pointer */ if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) { /* We need a filename when uploading. Return error! */ @@ -657,7 +659,7 @@ while(!*ftpcodep && !result) { /* check and reset timeout value every lap */ - timediff_t timeout = Curl_pp_state_timeout(data, pp); + timediff_t timeout = Curl_pp_state_timeleft_ms(data, pp); timediff_t interval_ms; if(timeout <= 0) { @@ -677,7 +679,7 @@ * A caution here is that the ftp_readresp() function has a cache that may * contain pieces of a response from the previous invoke and we need to * make sure we do not wait for input while there is unhandled data in - * that cache. But also, if the cache is there, we call ftp_readresp() and + * that cache. Also, if the cache is there, we call ftp_readresp() and * the cache was not good enough to continue we must not busy-loop around * this function. * @@ -730,7 +732,7 @@ } /* while there is buffer left and loop is requested */ pp->pending_resp = FALSE; - CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zd, ftpcode=%d", + CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zu, ftpcode=%d", result, *nreadp, *ftpcodep); return result; @@ -1060,8 +1062,9 @@ CURLcode result; *resp = NULL; - result = Curl_resolv_blocking(data, host, 0, conn->ip_version, - dns_entryp); + result = Curl_resolv_blocking( + data, Curl_resolv_dns_queries(data, conn->ip_version), + host, 0, Curl_conn_get_transport(data, conn), dns_entryp); if(result) failf(data, "failed to resolve the address provided to PORT: %s", host); else { @@ -1385,7 +1388,7 @@ /* cleanup */ if(dns_entry) - Curl_resolv_unlink(data, &dns_entry); + Curl_dns_entry_unlink(data, &dns_entry); if(result) { ftp_state(data, ftpc, FTP_STOP); } @@ -1559,7 +1562,7 @@ char *cmd; if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) { - /* url-decode before evaluation: e.g. paths starting/ending with %2f */ + /* URL-decode before evaluation: e.g. paths starting/ending with %2f */ const char *rawPath = ftpc->rawpath; const char *slashPos = strrchr(rawPath, '/'); if(slashPos) { @@ -1683,17 +1686,17 @@ if((data->state.resume_from && !sizechecked) || ((data->state.resume_from > 0) && sizechecked)) { - /* we are about to continue the uploading of a file */ - /* 1. get already existing file's size. We use the SIZE command for this - which may not exist in the server! The SIZE command is not in - RFC959. */ + /* we are about to continue the uploading of a file + 1. get already existing file's size. We use the SIZE command for this + which may not exist in the server! The SIZE command is not in + RFC959. - /* 2. This used to set REST. But since we can do append, we issue no - another ftp command. Skip the source file offset and APPEND the rest on - the file instead */ + 2. This used to set REST, but since we can do append, we issue no + another ftp command. Skip the source file offset and APPEND the rest + on the file instead - /* 3. pass file-size number of bytes in the source file */ - /* 4. lower the infilesize counter */ + 3. pass file-size number of bytes in the source file + 4. lower the infilesize counter */ /* => transfer as usual */ int seekerr = CURL_SEEKFUNC_OK; @@ -1942,8 +1945,8 @@ behavior. In addition: asking for the size for 'TYPE A' transfers is not - constructive since servers do not report the converted size. So - skip it. + constructive since servers do not report the converted size. + Thus, skip it. */ result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file); if(!result) @@ -2159,9 +2162,10 @@ if(result) goto error; - (void)Curl_resolv_blocking(data, host_name, ipquad.remote_port, - is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4, - &dns); + (void)Curl_resolv_blocking( + data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A, + host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn), + &dns); /* we connect to the proxy's port */ connectport = (unsigned short)ipquad.remote_port; @@ -2185,7 +2189,9 @@ goto error; } - (void)Curl_resolv_blocking(data, newhost, newport, conn->ip_version, &dns); + (void)Curl_resolv_blocking( + data, Curl_resolv_dns_queries(data, conn->ip_version), + newhost, newport, Curl_conn_get_transport(data, conn), &dns); connectport = newport; /* we connect to the remote port */ if(!dns) { @@ -2195,6 +2201,12 @@ } } + DEBUGASSERT(newhost); + curlx_free(conn->secondaryhostname); + conn->secondary_port = newport; + conn->secondaryhostname = newhost; + newhost = NULL; + result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns, conn->bits.ftp_use_data_ssl ? CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE); @@ -2202,10 +2214,8 @@ if(result) { if((result != CURLE_OUT_OF_MEMORY) && (ftpc->count1 == 0) && (ftpcode == 229)) { - curlx_free(newhost); - return ftp_epsv_disable(data, ftpc, conn); + result = ftp_epsv_disable(data, ftpc, conn); } - goto error; } @@ -2222,22 +2232,16 @@ */ char buf[256]; Curl_printable_address(dns->addr, buf, sizeof(buf)); - infof(data, "Connecting to %s (%s) port %d", newhost, buf, connectport); + infof(data, "Connecting to %s (%s) port %d", + conn->secondaryhostname, buf, connectport); } #endif - curlx_free(conn->secondaryhostname); - conn->secondary_port = newport; - conn->secondaryhostname = curlx_strdup(newhost); - if(!conn->secondaryhostname) { - result = CURLE_OUT_OF_MEMORY; - goto error; - } - conn->bits.do_more = TRUE; ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */ error: + Curl_dns_entry_unlink(data, &dns); curlx_free(newhost); return result; } @@ -2382,10 +2386,10 @@ else if((data->state.list_only || !ftpc->file) && !(data->set.prequote)) { /* The specified path ends with a slash, and therefore we think this - is a directory that is requested, use LIST. But before that we + is a directory that is requested, use LIST. Before that, we also need to set ASCII transfer mode. */ - /* But only if a body transfer was requested. */ + /* Only if a body transfer was requested. */ if(ftp->transfer == PPTRANSFER_BODY) { result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE); if(result) @@ -2490,24 +2494,32 @@ return result; } -static int twodigit(const char *p) +/* return TRUE on error, FALSE on success */ +static bool twodigit(const char *p, int *val) { - return ((p[0] - '0') * 10) + (p[1] - '0'); + if(!ISDIGIT(p[0]) || !ISDIGIT(p[1])) + return TRUE; + /* curlx_hexval() works fine here since we make sure it is decimal above */ + *val = (curlx_hexval(p[0]) * 10) + curlx_hexval(p[1]); + return FALSE; } -static bool ftp_213_date(const char *p, int *year, int *month, int *day, - int *hour, int *minute, int *second) +/* + * @unittest 1668 + */ +UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second); +UNITTEST bool ftp_213_date(const char *p, int *year, int *month, int *day, + int *hour, int *minute, int *second) { - size_t len = strlen(p); - if(len < 14) + int century; + if((strlen(p) < 14) || twodigit(&p[0], ¢ury) || twodigit(&p[2], year) || + twodigit(&p[4], month) || twodigit(&p[6], day) || + twodigit(&p[8], hour) || twodigit(&p[10], minute) || + twodigit(&p[12], second)) return FALSE; - *year = (twodigit(&p[0]) * 100) + twodigit(&p[2]); - *month = twodigit(&p[4]); - *day = twodigit(&p[6]); - *hour = twodigit(&p[8]); - *minute = twodigit(&p[10]); - *second = twodigit(&p[12]); + *year += century * 100; if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) || (*second > 60)) return FALSE; @@ -2568,7 +2580,7 @@ /* If we asked for a time of the file and we actually got one as well, we "emulate" an HTTP-style header in our output. */ -#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) +#if defined(CURL_HAVE_DIAG) && (defined(__DJGPP__) || defined(__AMIGA__)) #pragma GCC diagnostic push /* 'time_t' is unsigned in MSDOS and AmigaOS. Silence: warning: comparison of unsigned expression in '>= 0' is always true */ @@ -2576,7 +2588,7 @@ #endif if(data->req.no_body && ftpc->file && data->set.get_filetime && showtime) { -#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__)) +#if defined(CURL_HAVE_DIAG) && (defined(__DJGPP__) || defined(__AMIGA__)) #pragma GCC diagnostic pop #endif char headerbuf[128]; @@ -3078,10 +3090,18 @@ break; /* get out of this loop */ } } - else + else { + if(ISCNTRL(*ptr)) { + /* control characters have no business in a path */ + curlx_dyn_free(&out); + return CURLE_WEIRD_SERVER_REPLY; + } result = curlx_dyn_addn(&out, ptr, 1); - if(result) + } + if(result) { + curlx_dyn_free(&out); return result; + } } } if(entry_extracted) { @@ -3709,7 +3729,7 @@ if(data->set.ftp_filemethod == FTPFILE_NOCWD) pathLen = 0; /* relative path => working directory is FTP home */ else - /* file is url-decoded */ + /* file is URL-decoded */ pathLen -= ftpc->file ? strlen(ftpc->file) : 0; ftpc->prevpath = curlx_memdup0(rawPath, pathLen); } @@ -3867,12 +3887,12 @@ * This is the actual DO function for FTP. Get a file/directory according to * the options previously setup. */ -static -CURLcode ftp_perform(struct Curl_easy *data, - struct ftp_conn *ftpc, - struct FTP *ftp, - bool *connected, /* connect status after PASV / PORT */ - bool *dophase_done) +static CURLcode ftp_perform( + struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp, + bool *connected, /* connect status after PASV / PORT */ + bool *dophase_done) { /* this is FTP and no proxy */ CURLcode result = CURLE_OK; @@ -4006,7 +4026,7 @@ Curl_ftp_parselist_data_free(&ftpwc->parser); curlx_free(ftpwc); } - Curl_safefree(wildcard->pattern); + curlx_safefree(wildcard->pattern); wildcard->dtor = ZERO_NULL; wildcard->ftpwc = NULL; return result; @@ -4341,7 +4361,7 @@ struct FTP *ftp = entry; (void)key; (void)klen; - Curl_safefree(ftp->pathalloc); + curlx_safefree(ftp->pathalloc); curlx_free(ftp); } @@ -4351,11 +4371,11 @@ (void)key; (void)klen; freedirs(ftpc); - Curl_safefree(ftpc->account); - Curl_safefree(ftpc->alternative_to_user); - Curl_safefree(ftpc->entrypath); - Curl_safefree(ftpc->prevpath); - Curl_safefree(ftpc->server_os); + curlx_safefree(ftpc->account); + curlx_safefree(ftpc->alternative_to_user); + curlx_safefree(ftpc->entrypath); + curlx_safefree(ftpc->prevpath); + curlx_safefree(ftpc->server_os); Curl_pp_disconnect(&ftpc->pp); curlx_free(ftpc); } @@ -4418,7 +4438,7 @@ ftpc->alternative_to_user = curlx_strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]); if(!ftpc->alternative_to_user) { - Curl_safefree(ftpc->account); + curlx_safefree(ftpc->account); Curl_conn_meta_remove(conn, CURL_META_FTP_CONN); return CURLE_OUT_OF_MEMORY; } @@ -4457,7 +4477,7 @@ /* * FTP protocol. */ -static const struct Curl_protocol Curl_protocol_ftp = { +const struct Curl_protocol Curl_protocol_ftp = { ftp_setup_connection, /* setup_connection */ ftp_do, /* do_it */ ftp_done, /* done */ @@ -4472,46 +4492,9 @@ ftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif /* CURL_DISABLE_FTP */ - -/* - * FTP protocol handler. - */ -const struct Curl_scheme Curl_scheme_ftp = { - "ftp", /* scheme */ -#ifdef CURL_DISABLE_FTP - ZERO_NULL, -#else - &Curl_protocol_ftp, -#endif - CURLPROTO_FTP, /* protocol */ - CURLPROTO_FTP, /* family */ - PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | - PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | - PROTOPT_WILDCARD | PROTOPT_SSL_REUSE | - PROTOPT_CONN_REUSE, /* flags */ - PORT_FTP, /* defport */ -}; - -/* - * FTPS protocol handler. - */ -const struct Curl_scheme Curl_scheme_ftps = { - "ftps", /* scheme */ -#if defined(CURL_DISABLE_FTP) || !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_ftp, -#endif - CURLPROTO_FTPS, /* protocol */ - CURLPROTO_FTP, /* family */ - PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | - PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD | - PROTOPT_CONN_REUSE, /* flags */ - PORT_FTPS, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/ftp.h b/Utilities/cmcurl/lib/ftp.h index 20b3600..257f595 100644 --- a/Utilities/cmcurl/lib/ftp.h +++ b/Utilities/cmcurl/lib/ftp.h
@@ -25,73 +25,11 @@ ***************************************************************************/ #include "curl_setup.h" -#include "pingpong.h" - -extern const struct Curl_scheme Curl_scheme_ftp; -extern const struct Curl_scheme Curl_scheme_ftps; - #ifndef CURL_DISABLE_FTP +extern const struct Curl_protocol Curl_protocol_ftp; + bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn); -#endif /* CURL_DISABLE_FTP */ - -/**************************************************************************** - * FTP unique setup - ***************************************************************************/ -enum { - FTP_STOP, /* do nothing state, stops the state machine */ - FTP_WAIT220, /* waiting for the initial 220 response immediately after - a connect */ - FTP_AUTH, - FTP_USER, - FTP_PASS, - FTP_ACCT, - FTP_PBSZ, - FTP_PROT, - FTP_CCC, - FTP_PWD, - FTP_SYST, - FTP_NAMEFMT, - FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ - FTP_RETR_PREQUOTE, - FTP_STOR_PREQUOTE, - FTP_LIST_PREQUOTE, - FTP_POSTQUOTE, - FTP_CWD, /* change directory */ - FTP_MKD, /* if the directory did not exist */ - FTP_MDTM, /* to figure out the datestamp */ - FTP_TYPE, /* to set type when doing a head-like request */ - FTP_LIST_TYPE, /* set type when about to do a directory list */ - FTP_RETR_LIST_TYPE, - FTP_RETR_TYPE, /* set type when about to RETR a file */ - FTP_STOR_TYPE, /* set type when about to STOR a file */ - FTP_SIZE, /* get the remote file's size for head-like request */ - FTP_RETR_SIZE, /* get the remote file's size for RETR */ - FTP_STOR_SIZE, /* get the size for STOR */ - FTP_REST, /* when used to check if the server supports it in head-like */ - FTP_RETR_REST, /* when asking for "resume" in for RETR */ - FTP_PORT, /* generic state for PORT, LPRT and EPRT, check count1 */ - FTP_PRET, /* generic state for PRET RETR, PRET STOR and PRET LIST/NLST */ - FTP_PASV, /* generic state for PASV and EPSV, check count1 */ - FTP_LIST, /* generic state for LIST, NLST or a custom list command */ - FTP_RETR, - FTP_STOR, /* generic state for STOR and APPE */ - FTP_QUIT, - FTP_LAST /* never used */ -}; -typedef unsigned char ftpstate; /* use the enum values */ - -struct ftp_parselist_data; /* defined later in ftplistparser.c */ - -struct ftp_wc { - struct ftp_parselist_data *parser; - - struct { - curl_write_callback write_function; - FILE *file_descriptor; - } backup; -}; - typedef enum { FTPFILE_MULTICWD = 1, /* as defined by RFC1738 */ FTPFILE_NOCWD = 2, /* use SIZE / RETR / STOR on the full path */ @@ -99,74 +37,7 @@ file */ } curl_ftpfile; -/* This FTP struct is used in the Curl_easy. All FTP data that is - connection-oriented must be in FTP_conn to properly deal with the fact that - perhaps the Curl_easy is changed between the times the connection is - used. */ -struct FTP { - char *path; /* points to the urlpieces struct field */ - char *pathalloc; /* if non-NULL a pointer to an allocated path */ - - /* transfer a file/body or not, done as a typedefed enum to make debuggers - display the full symbol and not the numerical value */ - curl_pp_transfer transfer; - curl_off_t downloadsize; -}; - -/* one struct entry for each path component (of 'rawpath') */ -struct pathcomp { - int start; /* start column */ - int len; /* length in bytes */ -}; - -/* ftp_conn is used for struct connection-oriented data in the connectdata - struct */ -struct ftp_conn { - struct pingpong pp; - char *account; - char *alternative_to_user; - char *entrypath; /* the PWD reply when we logged on */ - const char *file; /* url-decoded filename (or path), points into rawpath */ - char *rawpath; /* URL decoded, allocated, version of the path */ - struct pathcomp *dirs; /* allocated array for path components */ - char *prevpath; /* url-decoded conn->path from the previous transfer */ - char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a - and others (A/I or zero) */ - char *server_os; /* The target server operating system. */ - curl_off_t known_filesize; /* file size is different from -1, if wildcard - LIST parsing was done and wc_statemach set - it */ - int count1; /* general purpose counter for the state machine */ - int count2; /* general purpose counter for the state machine */ - int count3; /* general purpose counter for the state machine */ - unsigned short dirdepth; /* number of entries used in the 'dirs' array, - < FTP_MAX_DIR_DEPTH */ - unsigned short cwdcount; /* number of CWD commands issued, - < FTP_MAX_DIR_DEPTH */ - unsigned char state; /* (ftpstate enum) always use ftp.c:state() to change - state! */ - unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or - IMAP or POP3 or others! (type: curl_usessl)*/ - unsigned char ccc; /* ccc level for this connection */ - BIT(ftp_trying_alternative); - BIT(dont_check); /* Set to TRUE to prevent the final (post-transfer) - file size and 226/250 status check. It should still - read the line, ignore the result. */ - BIT(ctl_valid); /* Tells Curl_ftp_quit() whether or not to do anything. If - the connection has timed out or been closed, this - should be FALSE when it gets to Curl_ftp_quit() */ - BIT(cwddone); /* if it has been determined that the proper CWD combo - already has been done */ - BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent - caching the current directory */ - BIT(wait_data_conn); /* this is set TRUE if data connection is waited */ - BIT(shutdown); /* connection is being shutdown, e.g. QUIT */ -}; - -/* meta key for storing `struct FTP` as easy meta data */ -#define CURL_META_FTP_EASY "meta:proto:ftp:easy" -/* meta key for storing `struct ftp_conn` as connection meta data */ -#define CURL_META_FTP_CONN "meta:proto:ftp:conn" +#endif /* CURL_DISABLE_FTP */ #define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c index f82895d..b5d9338 100644 --- a/Utilities/cmcurl/lib/ftplistparser.c +++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -44,6 +44,7 @@ #include "fileinfo.h" #include "llist.h" #include "ftp.h" +#include "ftp-int.h" #include "ftplistparser.h" #include "curl_fnmatch.h" #include "multiif.h" @@ -324,8 +325,7 @@ /* filter pattern-corresponding filenames */ Curl_set_in_callback(data, TRUE); - if(compare(data->set.fnmatch_data, wc->pattern, - finfo->filename) == 0) { + if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) { /* discard symlink which is containing multiple " -> " */ if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && (strstr(finfo->strings.target, " -> "))) {
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c index dedafd3..fab63e6 100644 --- a/Utilities/cmcurl/lib/getinfo.c +++ b/Utilities/cmcurl/lib/getinfo.c
@@ -78,6 +78,7 @@ info->conn_scheme = 0; info->conn_protocol = 0; + info->used_proxy = 0; #ifdef USE_SSL Curl_ssl_free_certinfo(data); @@ -414,6 +415,9 @@ case CURLINFO_FILETIME_T: *param_offt = (curl_off_t)data->info.filetime; break; + case CURLINFO_SIZE_DELIVERED: + *param_offt = data->progress.deliver; + break; case CURLINFO_SIZE_UPLOAD_T: *param_offt = data->progress.ul.cur_size; break; @@ -582,14 +586,16 @@ break; case CURLINFO_TLS_SESSION: case CURLINFO_TLS_SSL_PTR: { + int query = (info == CURLINFO_TLS_SSL_PTR) ? + CF_QUERY_SSL_INFO : CF_QUERY_SSL_CTX_INFO; struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **) - param_slistp; + param_slistp; struct curl_tlssessioninfo *tsi = &data->tsi; /* we are exposing a pointer to internal memory with unknown * lifetime here. */ *tsip = tsi; - if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, tsi)) { + if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, query, tsi)) { tsi->backend = Curl_ssl_backend(); tsi->internals = NULL; }
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c index 71ff392..f087121 100644 --- a/Utilities/cmcurl/lib/gopher.c +++ b/Utilities/cmcurl/lib/gopher.c
@@ -172,7 +172,7 @@ * connect-command-download protocols. */ -static const struct Curl_protocol Curl_protocol_gopher = { +const struct Curl_protocol Curl_protocol_gopher = { ZERO_NULL, /* setup_connection */ gopher_do, /* do_it */ ZERO_NULL, /* done */ @@ -187,13 +187,13 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #ifdef USE_SSL -static const struct Curl_protocol Curl_protocol_gophers = { +const struct Curl_protocol Curl_protocol_gophers = { ZERO_NULL, /* setup_connection */ gopher_do, /* do_it */ ZERO_NULL, /* done */ @@ -208,36 +208,10 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif #endif /* CURL_DISABLE_GOPHER */ - -const struct Curl_scheme Curl_scheme_gopher = { - "gopher", /* scheme */ -#ifdef CURL_DISABLE_GOPHER - ZERO_NULL, -#else - &Curl_protocol_gopher, -#endif - CURLPROTO_GOPHER, /* protocol */ - CURLPROTO_GOPHER, /* family */ - PROTOPT_NONE, /* flags */ - PORT_GOPHER, /* defport */ -}; - -const struct Curl_scheme Curl_scheme_gophers = { - "gophers", /* scheme */ -#if defined(CURL_DISABLE_GOPHER) || !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_gophers, -#endif - CURLPROTO_GOPHERS, /* protocol */ - CURLPROTO_GOPHER, /* family */ - PROTOPT_SSL, /* flags */ - PORT_GOPHER, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/gopher.h b/Utilities/cmcurl/lib/gopher.h index bc977bc..1c126be 100644 --- a/Utilities/cmcurl/lib/gopher.h +++ b/Utilities/cmcurl/lib/gopher.h
@@ -23,7 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_gopher; -extern const struct Curl_scheme Curl_scheme_gophers; +#ifndef CURL_DISABLE_GOPHER +extern const struct Curl_protocol Curl_protocol_gopher; +extern const struct Curl_protocol Curl_protocol_gophers; +#endif #endif /* HEADER_CURL_GOPHER_H */
diff --git a/Utilities/cmcurl/lib/hash.c b/Utilities/cmcurl/lib/hash.c index 8095ca2..3eae1d2 100644 --- a/Utilities/cmcurl/lib/hash.c +++ b/Utilities/cmcurl/lib/hash.c
@@ -265,7 +265,7 @@ DEBUGASSERT(h->init == HASHINIT); if(h->table) { Curl_hash_clean(h); - Curl_safefree(h->table); + curlx_safefree(h->table); } DEBUGASSERT(h->size == 0); h->slots = 0; @@ -359,8 +359,8 @@ #endif } -struct Curl_hash_element * -Curl_hash_next_element(struct Curl_hash_iterator *iter) +struct Curl_hash_element *Curl_hash_next_element( + struct Curl_hash_iterator *iter) { struct Curl_hash *h; DEBUGASSERT(iter->init == ITERINIT);
diff --git a/Utilities/cmcurl/lib/hash.h b/Utilities/cmcurl/lib/hash.h index 267f91d..1d7d3de 100644 --- a/Utilities/cmcurl/lib/hash.h +++ b/Utilities/cmcurl/lib/hash.h
@@ -97,8 +97,8 @@ size_t key2_len); void Curl_hash_start_iterate(struct Curl_hash *hash, struct Curl_hash_iterator *iter); -struct Curl_hash_element * -Curl_hash_next_element(struct Curl_hash_iterator *iter); +struct Curl_hash_element *Curl_hash_next_element( + struct Curl_hash_iterator *iter); void Curl_hash_print(struct Curl_hash *h, void (*func)(void *));
diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c index 5a6b120..195e12b 100644 --- a/Utilities/cmcurl/lib/headers.c +++ b/Utilities/cmcurl/lib/headers.c
@@ -52,23 +52,23 @@ } /* public API */ -CURLHcode curl_easy_header(CURL *easy, +CURLHcode curl_easy_header(CURL *curl, const char *name, size_t nameindex, - unsigned int type, + unsigned int origin, int request, struct curl_header **hout) { struct Curl_llist_node *e; struct Curl_llist_node *e_pick = NULL; - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; size_t match = 0; size_t amount = 0; struct Curl_header_store *hs = NULL; struct Curl_header_store *pick = NULL; if(!name || !hout || !data || - (type > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | - CURLH_PSEUDO)) || !type || (request < -1)) + (origin > (CURLH_HEADER | CURLH_TRAILER | CURLH_CONNECT | CURLH_1XX | + CURLH_PSEUDO)) || !origin || (request < -1)) return CURLHE_BAD_ARGUMENT; if(!Curl_llist_count(&data->state.httphdrs)) return CURLHE_NOHEADERS; /* no headers available */ @@ -81,7 +81,7 @@ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); if(curl_strequal(hs->name, name) && - (hs->type & type) && + (hs->type & origin) && (hs->request == request)) { amount++; pick = hs; @@ -100,7 +100,7 @@ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); if(curl_strequal(hs->name, name) && - (hs->type & type) && + (hs->type & origin) && (hs->request == request) && (match++ == nameindex)) { e_pick = e; @@ -118,12 +118,12 @@ } /* public API */ -struct curl_header *curl_easy_nextheader(CURL *easy, - unsigned int type, +struct curl_header *curl_easy_nextheader(CURL *curl, + unsigned int origin, int request, struct curl_header *prev) { - struct Curl_easy *data = easy; + struct Curl_easy *data = curl; struct Curl_llist_node *pick; struct Curl_llist_node *e; struct Curl_header_store *hs; @@ -149,7 +149,7 @@ /* make sure it is the next header of the desired type */ do { hs = Curl_node_elem(pick); - if((hs->type & type) && (hs->request == request)) + if((hs->type & origin) && (hs->request == request)) break; pick = Curl_node_next(pick); } while(pick); @@ -167,7 +167,7 @@ struct Curl_header_store *check = Curl_node_elem(e); if(curl_strequal(hs->name, check->name) && (check->request == request) && - (check->type & type)) + (check->type & origin)) amount++; if(e == pick) index = amount - 1;
diff --git a/Utilities/cmcurl/lib/headers.h b/Utilities/cmcurl/lib/headers.h index 85905e5..283179a 100644 --- a/Utilities/cmcurl/lib/headers.h +++ b/Utilities/cmcurl/lib/headers.h
@@ -38,7 +38,7 @@ /* * Initialize header collecting for a transfer. - * Will add a client writer that catches CLIENTWRITE_HEADER writes. + * Add a client writer that catches CLIENTWRITE_HEADER writes. */ CURLcode Curl_headers_init(struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c index 028b476..a4d6ebd 100644 --- a/Utilities/cmcurl/lib/hmac.c +++ b/Utilities/cmcurl/lib/hmac.c
@@ -25,8 +25,8 @@ ***************************************************************************/ #include "curl_setup.h" -#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ - !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ +#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \ + !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \ defined(USE_SSL) #include "curl_hmac.h" @@ -98,13 +98,12 @@ return NULL; } -int Curl_HMAC_update(struct HMAC_context *ctxt, - const unsigned char *data, - unsigned int len) +void Curl_HMAC_update(struct HMAC_context *ctxt, + const unsigned char *data, + unsigned int len) { /* Update first hash calculation. */ ctxt->hash->hupdate(ctxt->hashctxt1, data, len); - return 0; } int Curl_HMAC_final(struct HMAC_context *ctxt, unsigned char *output) @@ -143,17 +142,24 @@ */ CURLcode Curl_hmacit(const struct HMAC_params *hashparams, const unsigned char *key, const size_t keylen, - const unsigned char *data, const size_t datalen, + const unsigned char *data, size_t datalen, unsigned char *output) { - struct HMAC_context *ctxt = - Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen)); + struct HMAC_context *ctxt; + if(keylen > UINT_MAX) /* unlikely to ever happen */ + return CURLE_BAD_FUNCTION_ARGUMENT; + ctxt = Curl_HMAC_init(hashparams, key, curlx_uztoui(keylen)); if(!ctxt) return CURLE_OUT_OF_MEMORY; /* Update the digest with the given challenge */ - Curl_HMAC_update(ctxt, data, curlx_uztoui(datalen)); + do { + unsigned int ilen = (unsigned int)CURLMIN(datalen, UINT_MAX); + Curl_HMAC_update(ctxt, data, ilen); + datalen -= ilen; + data += ilen; + } while(datalen); /* Finalise the digest */ Curl_HMAC_final(ctxt, output);
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c index 542b655..85f53c4 100644 --- a/Utilities/cmcurl/lib/hostip.c +++ b/Utilities/cmcurl/lib/hostip.c
@@ -46,21 +46,18 @@ #include "urldata.h" #include "curl_addrinfo.h" #include "curl_trc.h" -#include "connect.h" +#include "dnscache.h" #include "hostip.h" -#include "hash.h" #include "httpsrr.h" -#include "rand.h" -#include "curl_share.h" #include "url.h" -#include "curlx/inet_ntop.h" -#include "curlx/inet_pton.h" #include "multiif.h" -#include "doh.h" #include "progress.h" +#include "doh.h" #include "select.h" #include "strcase.h" #include "easy_lock.h" +#include "curlx/inet_ntop.h" +#include "curlx/inet_pton.h" #include "curlx/strcopy.h" #include "curlx/strparse.h" @@ -77,6 +74,21 @@ #define MAX_DNS_CACHE_SIZE 29999 +#define RESOLV_FAIL(for_proxy) \ + ((for_proxy) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST) + +#define IS_RESOLV_FAIL(result) \ + (((result) == CURLE_COULDNT_RESOLVE_HOST) || \ + ((result) == CURLE_COULDNT_RESOLVE_PROXY)) +/* + * ipv6works() returns TRUE if IPv6 seems to work. + */ +#ifdef USE_IPV6 +static bool ipv6works(struct Curl_easy *data); +#else +#define ipv6works(x) FALSE +#endif + /* * hostip.c explained * ================== @@ -89,10 +101,10 @@ * take that into account. Hosts that are not IPv6-enabled have CURLRES_IPV4 * defined. * - * CURLRES_ARES - is defined if libcurl is built to use c-ares for + * USE_RESOLV_ARES - is defined if libcurl is built to use c-ares for * asynchronous name resolves. This can be Windows or *nix. * - * CURLRES_THREADED - is defined if libcurl is built to run under (native) + * USE_RESOLV_THREADED - is defined if libcurl is built to run under (native) * Windows, and then the name resolve will be done in a new thread, and the * supported API will be the same as for ares-builds. * @@ -114,85 +126,49 @@ * CURLRES_* defines based on the config*.h and curl_setup.h defines. */ +uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version) +{ + (void)data; + switch(ip_version) { + case CURL_IPRESOLVE_V6: + return CURL_DNSQ_AAAA; + case CURL_IPRESOLVE_V4: + return CURL_DNSQ_A; + default: + if(ipv6works(data)) + return (CURL_DNSQ_A | CURL_DNSQ_AAAA); + else + return CURL_DNSQ_A; + } +} + #ifdef CURLVERBOSE -static void show_resolve_info(struct Curl_easy *data, - struct Curl_dns_entry *dns) +const char *Curl_resolv_query_str(uint8_t dns_queries) { - const struct Curl_addrinfo *a; - CURLcode result = CURLE_OK; -#ifdef CURLRES_IPV6 - struct dynbuf out[2]; -#else - struct dynbuf out[1]; -#endif - DEBUGASSERT(data); - DEBUGASSERT(dns); - - if(!data->set.verbose || - /* ignore no name or numerical IP addresses */ - !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname)) - return; - - a = dns->addr; - - infof(data, "Host %s:%d was resolved.", - (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport); - - curlx_dyn_init(&out[0], 1024); -#ifdef CURLRES_IPV6 - curlx_dyn_init(&out[1], 1024); -#endif - - while(a) { - if( -#ifdef CURLRES_IPV6 - a->ai_family == PF_INET6 || -#endif - a->ai_family == PF_INET) { - char buf[MAX_IPADR_LEN]; - struct dynbuf *d = &out[(a->ai_family != PF_INET)]; - Curl_printable_address(a, buf, sizeof(buf)); - if(curlx_dyn_len(d)) - result = curlx_dyn_addn(d, ", ", 2); - if(!result) - result = curlx_dyn_add(d, buf); - if(result) { - infof(data, "too many IP, cannot show"); - goto fail; - } - } - a = a->ai_next; + switch(dns_queries) { + case (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS): + return "A+AAAA+HTTPS"; + case (CURL_DNSQ_A | CURL_DNSQ_AAAA): + return "A+AAAA"; + case (CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS): + return "AAAA+HTTPS"; + case (CURL_DNSQ_AAAA): + return "AAAA"; + case (CURL_DNSQ_A | CURL_DNSQ_HTTPS): + return "A+HTTPS"; + case (CURL_DNSQ_A): + return "A"; + case (CURL_DNSQ_HTTPS): + return "HTTPS"; + case 0: + return "-"; + default: + DEBUGASSERT(0); + return "???"; } - -#ifdef CURLRES_IPV6 - infof(data, "IPv6: %s", - (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)")); -#endif - infof(data, "IPv4: %s", - (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)")); - -fail: - curlx_dyn_free(&out[0]); -#ifdef CURLRES_IPV6 - curlx_dyn_free(&out[1]); -#endif } -#else -#define show_resolve_info(x, y) Curl_nop_stmt #endif -static void dnscache_entry_free(struct Curl_dns_entry *dns) -{ - Curl_freeaddrinfo(dns->addr); -#ifdef USE_HTTPSRR - if(dns->hinfo) { - Curl_httpsrr_cleanup(dns->hinfo); - curlx_free(dns->hinfo); - } -#endif - curlx_free(dns); -} - /* * Curl_printable_address() stores a printable version of the 1st address * given in the 'ai' argument. The result will be stored in the buf that is @@ -226,449 +202,17 @@ } } -/* - * Create a hostcache id string for the provided host + port, to be used by - * the DNS caching. Without alloc. Return length of the id string. - */ -static size_t create_dnscache_id(const char *name, - size_t nlen, /* 0 or actual name length */ - int port, char *ptr, size_t buflen) -{ - size_t len = nlen ? nlen : strlen(name); - DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN); - if(len > (buflen - 7)) - len = buflen - 7; - /* store and lower case the name */ - Curl_strntolower(ptr, name, len); - return curl_msnprintf(&ptr[len], 7, ":%u", port) + len; -} - -struct dnscache_prune_data { - struct curltime now; - timediff_t oldest_ms; /* oldest time in cache not pruned. */ - timediff_t max_age_ms; -}; - -/* - * This function is set as a callback to be called for every entry in the DNS - * cache when we want to prune old unused entries. - * - * Returning non-zero means remove the entry, return 0 to keep it in the - * cache. - */ -static int dnscache_entry_is_stale(void *datap, void *hc) -{ - struct dnscache_prune_data *prune = (struct dnscache_prune_data *)datap; - struct Curl_dns_entry *dns = (struct Curl_dns_entry *)hc; - - if(dns->timestamp.tv_sec || dns->timestamp.tv_usec) { - /* get age in milliseconds */ - timediff_t age = curlx_ptimediff_ms(&prune->now, &dns->timestamp); - if(!dns->addr) - age *= 2; /* negative entries age twice as fast */ - if(age >= prune->max_age_ms) - return TRUE; - if(age > prune->oldest_ms) - prune->oldest_ms = age; - } - return FALSE; -} - -/* - * Prune the DNS cache. This assumes that a lock has already been taken. - * Returns the 'age' of the oldest still kept entry - in milliseconds. - */ -static timediff_t dnscache_prune(struct Curl_hash *hostcache, - timediff_t cache_timeout_ms, - struct curltime now) -{ - struct dnscache_prune_data user; - - user.max_age_ms = cache_timeout_ms; - user.now = now; - user.oldest_ms = 0; - - Curl_hash_clean_with_criterium(hostcache, - (void *)&user, - dnscache_entry_is_stale); - - return user.oldest_ms; -} - -static struct Curl_dnscache *dnscache_get(struct Curl_easy *data) -{ - if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) - return &data->share->dnscache; - if(data->multi) - return &data->multi->dnscache; - return NULL; -} - -static void dnscache_lock(struct Curl_easy *data, - struct Curl_dnscache *dnscache) -{ - if(data->share && dnscache == &data->share->dnscache) - Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); -} - -static void dnscache_unlock(struct Curl_easy *data, - struct Curl_dnscache *dnscache) -{ - if(data->share && dnscache == &data->share->dnscache) - Curl_share_unlock(data, CURL_LOCK_DATA_DNS); -} - -/* - * Library-wide function for pruning the DNS cache. This function takes and - * returns the appropriate locks. - */ -void Curl_dnscache_prune(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - /* the timeout may be set -1 (forever) */ - timediff_t timeout_ms = data->set.dns_cache_timeout_ms; - - if(!dnscache || (timeout_ms == -1)) - /* NULL hostcache means we cannot do it */ - return; - - dnscache_lock(data, dnscache); - - do { - /* Remove outdated and unused entries from the hostcache */ - timediff_t oldest_ms = - dnscache_prune(&dnscache->entries, timeout_ms, *Curl_pgrs_now(data)); - - if(Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE) - /* prune the ones over half this age */ - timeout_ms = oldest_ms / 2; - else - break; - - /* if the cache size is still too big, use the oldest age as new prune - limit */ - } while(timeout_ms); - - dnscache_unlock(data, dnscache); -} - -void Curl_dnscache_clear(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - if(dnscache) { - dnscache_lock(data, dnscache); - Curl_hash_clean(&dnscache->entries); - dnscache_unlock(data, dnscache); - } -} - #ifdef USE_ALARM_TIMEOUT /* Beware this is a global and unique instance. This is used to store the return address that we can jump back to from inside a signal handler. This is not thread-safe stuff. */ static sigjmp_buf curl_jmpenv; -static curl_simple_lock curl_jmpenv_lock; +static curl_simple_lock curl_jmpenv_lock = CURL_SIMPLE_LOCK_INIT; #endif -/* lookup address, returns entry if found and not stale */ -static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - const char *hostname, - int port, - int ip_version) -{ - struct Curl_dns_entry *dns = NULL; - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; - - if(!dnscache) - return NULL; - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, 0, port, - entry_id, sizeof(entry_id)); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - - /* No entry found in cache, check if we might have a wildcard entry */ - if(!dns && data->state.wildcard_resolve) { - entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id)); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - } - - if(dns && (data->set.dns_cache_timeout_ms != -1)) { - /* See whether the returned entry is stale. Done before we release lock */ - struct dnscache_prune_data user; - - user.now = *Curl_pgrs_now(data); - user.max_age_ms = data->set.dns_cache_timeout_ms; - user.oldest_ms = 0; - - if(dnscache_entry_is_stale(&user, dns)) { - infof(data, "Hostname in DNS cache was stale, zapped"); - dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - } - - /* See if the returned entry matches the required resolve mode */ - if(dns && ip_version != CURL_IPRESOLVE_WHATEVER) { - int pf = PF_INET; - bool found = FALSE; - struct Curl_addrinfo *addr = dns->addr; - -#ifdef PF_INET6 - if(ip_version == CURL_IPRESOLVE_V6) - pf = PF_INET6; -#endif - - while(addr) { - if(addr->ai_family == pf) { - found = TRUE; - break; - } - addr = addr->ai_next; - } - - if(!found) { - infof(data, "Hostname in DNS cache does not have needed family, zapped"); - dns = NULL; /* the memory deallocation is being handled by the hash */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - } - return dns; -} - -/* - * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. - * - * Curl_resolv() checks initially and multi_runsingle() checks each time - * it discovers the handle in the state WAITRESOLVE whether the hostname - * has already been resolved and the address has already been stored in - * the DNS cache. This short circuits waiting for a lot of pending - * lookups for the same hostname requested by different handles. - * - * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -struct Curl_dns_entry *Curl_dnscache_get(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = NULL; - - dnscache_lock(data, dnscache); - - dns = fetch_addr(data, dnscache, hostname, port, ip_version); - if(dns) - dns->refcount++; /* we use it! */ - - dnscache_unlock(data, dnscache); - - return dns; -} - -#ifndef CURL_DISABLE_SHUFFLE_DNS -/* - * Return # of addresses in a Curl_addrinfo struct - */ -static int num_addresses(const struct Curl_addrinfo *addr) -{ - int i = 0; - while(addr) { - addr = addr->ai_next; - i++; - } - return i; -} - -UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - struct Curl_addrinfo **addr); -/* - * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' - * struct by re-linking its linked list. - * - * The addr argument should be the address of a pointer to the head node of a - * `Curl_addrinfo` list and it will be modified to point to the new head after - * shuffling. - * - * Not declared static only to make it easy to use in a unit test! - * - * @unittest: 1608 - */ -UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data, - struct Curl_addrinfo **addr) -{ - CURLcode result = CURLE_OK; - const int num_addrs = num_addresses(*addr); - - if(num_addrs > 1) { - struct Curl_addrinfo **nodes; - infof(data, "Shuffling %i addresses", num_addrs); - - nodes = curlx_malloc(num_addrs * sizeof(*nodes)); - if(nodes) { - int i; - unsigned int *rnd; - const size_t rnd_size = num_addrs * sizeof(*rnd); - - /* build a plain array of Curl_addrinfo pointers */ - nodes[0] = *addr; - for(i = 1; i < num_addrs; i++) { - nodes[i] = nodes[i - 1]->ai_next; - } - - rnd = curlx_malloc(rnd_size); - if(rnd) { - /* Fisher-Yates shuffle */ - if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { - struct Curl_addrinfo *swap_tmp; - for(i = num_addrs - 1; i > 0; i--) { - swap_tmp = nodes[rnd[i] % (unsigned int)(i + 1)]; - nodes[rnd[i] % (unsigned int)(i + 1)] = nodes[i]; - nodes[i] = swap_tmp; - } - - /* relink list in the new order */ - for(i = 1; i < num_addrs; i++) { - nodes[i - 1]->ai_next = nodes[i]; - } - - nodes[num_addrs - 1]->ai_next = NULL; - *addr = nodes[0]; - } - curlx_free(rnd); - } - else - result = CURLE_OUT_OF_MEMORY; - curlx_free(nodes); - } - else - result = CURLE_OUT_OF_MEMORY; - } - return result; -} -#endif - -struct Curl_dns_entry * -Curl_dnscache_mk_entry(struct Curl_easy *data, - struct Curl_addrinfo **paddr, - const char *hostname, - size_t hostlen, /* length or zero */ - int port, - bool permanent) -{ - struct Curl_dns_entry *dns = NULL; - -#ifndef CURL_DISABLE_SHUFFLE_DNS - /* shuffle addresses if requested */ - if(data->set.dns_shuffle_addresses && paddr) { - CURLcode result = Curl_shuffle_addr(data, paddr); - if(result) - goto out; - } -#else - (void)data; -#endif - if(!hostlen) - hostlen = strlen(hostname); - - /* Create a new cache entry */ - dns = curlx_calloc(1, sizeof(struct Curl_dns_entry) + hostlen); - if(!dns) - goto out; - - dns->refcount = 1; /* the cache has the first reference */ - dns->addr = paddr ? *paddr : NULL; /* this is the address(es) */ - if(permanent) { - dns->timestamp.tv_sec = 0; /* an entry that never goes stale */ - dns->timestamp.tv_usec = 0; /* an entry that never goes stale */ - } - else { - dns->timestamp = *Curl_pgrs_now(data); - } - dns->hostport = port; - if(hostlen) - memcpy(dns->hostname, hostname, hostlen); - -out: - if(paddr) { - if(!dns) - Curl_freeaddrinfo(*paddr); - *paddr = NULL; - } - return dns; -} - -static struct Curl_dns_entry * -dnscache_add_addr(struct Curl_easy *data, - struct Curl_dnscache *dnscache, - struct Curl_addrinfo **paddr, - const char *hostname, - size_t hlen, /* length or zero */ - int port, - bool permanent) -{ - char entry_id[MAX_HOSTCACHE_LEN]; - size_t entry_len; - struct Curl_dns_entry *dns; - struct Curl_dns_entry *dns2; - - dns = Curl_dnscache_mk_entry(data, paddr, hostname, hlen, port, permanent); - if(!dns) - return NULL; - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(hostname, hlen, port, - entry_id, sizeof(entry_id)); - - /* Store the resolved data in our DNS cache. */ - dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1, - (void *)dns); - if(!dns2) { - dnscache_entry_free(dns); - return NULL; - } - - dns = dns2; - dns->refcount++; /* mark entry as in-use */ - return dns; -} - -CURLcode Curl_dnscache_add(struct Curl_easy *data, - struct Curl_dns_entry *entry) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - char id[MAX_HOSTCACHE_LEN]; - size_t idlen; - - if(!dnscache) - return CURLE_FAILED_INIT; - /* Create an entry id, based upon the hostname and port */ - idlen = create_dnscache_id(entry->hostname, 0, entry->hostport, - id, sizeof(id)); - - /* Store the resolved data in our DNS cache and up ref count */ - dnscache_lock(data, dnscache); - if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) { - dnscache_unlock(data, dnscache); - return CURLE_OUT_OF_MEMORY; - } - entry->refcount++; - dnscache_unlock(data, dnscache); - return CURLE_OK; -} - #ifdef USE_IPV6 /* return a static IPv6 ::1 for the name */ -static struct Curl_addrinfo *get_localhost6(int port, const char *name) +static struct Curl_addrinfo *get_localhost6(uint16_t port, const char *name) { struct Curl_addrinfo *ca; const size_t ss_size = sizeof(struct sockaddr_in6); @@ -680,12 +224,9 @@ if(!ca) return NULL; + memset(&sa6, 0, sizeof(sa6)); sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(port16); - sa6.sin6_flowinfo = 0; -#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID - sa6.sin6_scope_id = 0; -#endif (void)curlx_inet_pton(AF_INET6, "::1", ipv6); memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6)); @@ -707,7 +248,7 @@ #endif /* return a static IPv4 127.0.0.1 for the given name */ -static struct Curl_addrinfo *get_localhost(int port, const char *name) +static struct Curl_addrinfo *get_localhost(uint16_t port, const char *name) { struct Curl_addrinfo *ca; struct Curl_addrinfo *ca6; @@ -766,9 +307,9 @@ } /* - * Curl_ipv6works() returns TRUE if IPv6 seems to work. + * ipv6works() returns TRUE if IPv6 seems to work. */ -bool Curl_ipv6works(struct Curl_easy *data) +static bool ipv6works(struct Curl_easy *data) { DEBUGASSERT(data); DEBUGASSERT(data->multi); @@ -804,86 +345,354 @@ return curl_strnequal(part, &full[flen - plen], plen); } -static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version) +static CURLcode hostip_resolv_failed(struct Curl_easy *data, + const char *hostname, + bool for_proxy) { -#ifdef CURLRES_IPV6 - if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data)) - return FALSE; -#elif defined(CURLRES_IPV4) + failf(data, "Could not resolve %s: %s", + for_proxy ? "proxy" : "host", hostname); + return RESOLV_FAIL(for_proxy); +} + +static bool can_resolve_dns_queries(struct Curl_easy *data, + uint8_t dns_queries) +{ (void)data; - if(ip_version == CURL_IPRESOLVE_V6) + if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && !ipv6works(data)) return FALSE; -#else -#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined -#endif return TRUE; } -static CURLcode store_negative_resolve(struct Curl_easy *data, - const char *host, - int port) +CURLcode Curl_resolv_announce_start(struct Curl_easy *data, + void *resolver) { - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns; - DEBUGASSERT(dnscache); - if(!dnscache) - return CURLE_FAILED_INIT; + if(data->set.resolver_start) { + int rc; - /* put this new host in the cache */ - dns = dnscache_add_addr(data, dnscache, NULL, host, 0, port, FALSE); - if(dns) { - /* release the returned reference; the cache itself will keep the - * entry alive: */ - dns->refcount--; - infof(data, "Store negative name resolve for %s:%d", host, port); - return CURLE_OK; + CURL_TRC_DNS(data, "announcing resolve to application"); + Curl_set_in_callback(data, TRUE); + rc = data->set.resolver_start(resolver, NULL, + data->set.resolver_start_client); + Curl_set_in_callback(data, FALSE); + if(rc) { + CURL_TRC_DNS(data, "application aborted resolve"); + return CURLE_ABORTED_BY_CALLBACK; + } } - return CURLE_OUT_OF_MEMORY; + return CURLE_OK; } -/* - * Curl_resolv() is the main name resolve function within libcurl. It resolves - * a name and returns a pointer to the entry in the 'entry' argument. This - * function might return immediately if we are using asynch resolves. See the - * return codes. - * - * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlink() later (when you are - * done using this struct) to decrease the reference counter again. - * - * Return codes: - * CURLE_OK = success, *entry set to non-NULL - * CURLE_AGAIN = resolving in progress, *entry == NULL - * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL - * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL - */ -CURLcode Curl_resolv(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - bool allowDOH, - struct Curl_dns_entry **entry) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *addr = NULL; - bool respwait = FALSE; - size_t hostname_len; - CURLcode result = CURLE_COULDNT_RESOLVE_HOST; +#ifdef USE_CURL_ASYNC - *entry = NULL; +static struct Curl_resolv_async *hostip_async_new(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms) +{ + struct Curl_resolv_async *async; + size_t hostlen = strlen(hostname); + + if(!data->multi) { + DEBUGASSERT(0); + return NULL; + } + + /* struct size already includes the NUL for hostname */ + async = curlx_calloc(1, sizeof(*async) + hostlen); + if(!async) + return NULL; + + /* Give every async resolve operation a "unique" id. This may + * wrap around after a long time, making collisions highly unlikely. + * As we keep the async structs at the easy handle, chances of + * easy `mid plus resolv->id` colliding should be astronomical. + * `resolv_id == 0` is never used. */ + if(data->multi->last_resolv_id == UINT32_MAX) + data->multi->last_resolv_id = 1; /* wrap around */ + else + data->multi->last_resolv_id++; + async->id = data->multi->last_resolv_id; + async->dns_queries = dns_queries; + async->port = port; + async->transport = transport; + async->for_proxy = for_proxy; + async->start = *Curl_pgrs_now(data); + async->timeout_ms = timeout_ms; + if(hostlen) { + memcpy(async->hostname, hostname, hostlen); + async->is_ipaddr = Curl_is_ipaddr(async->hostname); + if(async->is_ipaddr) + async->is_ipv4addr = Curl_is_ipv4addr(async->hostname); + } + + return async; +} + +static CURLcode hostip_resolv_take_result(struct Curl_easy *data, + struct Curl_resolv_async *async, + struct Curl_dns_entry **pdns) +{ + CURLcode result; + + /* If async resolving is ongoing, this must be set */ + if(!async) + return CURLE_FAILED_INIT; #ifndef CURL_DISABLE_DOH - data->conn->bits.doh = FALSE; /* default is not */ + if(async->doh) + result = Curl_doh_take_result(data, async, pdns); + else +#endif + result = Curl_async_take_result(data, async, pdns); + + if(result == CURLE_AGAIN) { + CURL_TRC_DNS(data, "resolve incomplete, queries=%s, responses=%s, " + "ongoing=%d for %s:%d", + Curl_resolv_query_str(async->dns_queries), + Curl_resolv_query_str(async->dns_responses), + async->queries_ongoing, async->hostname, async->port); + result = CURLE_OK; + } + else if(result) { + result = Curl_async_failed(data, async, NULL); + } + else { + CURL_TRC_DNS(data, "resolve complete for %s:%u", + async->hostname, async->port); + DEBUGASSERT(*pdns); + } + + return result; +} + +timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data, + uint32_t resolv_id) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(!async) + return CURL_TIMEOUT_RESOLVE_MS; + return curlx_ptimediff_ms(Curl_pgrs_now(data), &async->start); +} + +bool Curl_resolv_has_answers(struct Curl_easy *data, + uint32_t resolv_id, uint8_t dns_queries) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + uint8_t check_queries; + /* a no longer existing/running resolve has all answers. */ + if(!async || async->done) + return TRUE; + /* Relevant are only queries undertaken. Others are considered answered. */ + check_queries = (dns_queries & async->dns_queries); + if((check_queries & async->dns_responses) != check_queries) { + return FALSE; + } + return TRUE; +} + +const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, + uint32_t resolv_id, + int ai_family, + unsigned int index) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + (void)index; + if(!async) + return NULL; + if((ai_family == AF_INET) && !(async->dns_queries & CURL_DNSQ_A)) + return NULL; +#ifdef USE_IPV6 + if((ai_family == AF_INET6) && !(async->dns_queries & CURL_DNSQ_AAAA)) + return NULL; +#endif + return Curl_async_get_ai(data, async, ai_family, index); +} + + +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo * +Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(!async) + return NULL; + return Curl_async_get_https(data, async); +} + +bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); + if(!async) + return TRUE; + return Curl_async_knows_https(data, async); +} + +#endif /* USE_HTTPSRR */ + +#endif /* USE_CURL_ASYNC */ + +static CURLcode hostip_resolv_start(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + bool allowDOH, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) +{ +#ifdef USE_CURL_ASYNC + struct Curl_resolv_async *async = NULL; +#endif + struct Curl_addrinfo *addr = NULL; + size_t hostname_len; + CURLcode result = CURLE_OK; + + (void)timeout_ms; /* not in all ifdefs */ + *presolv_id = 0; + *pdns = NULL; + + /* Check for "known" things to resolve ourselves. */ +#ifndef USE_RESOLVE_ON_IPS + if(Curl_is_ipaddr(hostname)) { + /* test655 verifies that the announce is done, even though there + * is no real resolving. So, keep doing this. */ + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + /* shortcut literal IP addresses, if we are not told to resolve them. */ + result = Curl_str2addr(hostname, port, &addr); + goto out; + } +#endif + + hostname_len = strlen(hostname); + if(curl_strequal(hostname, "localhost") || + curl_strequal(hostname, "localhost.") || + tailmatch(hostname, hostname_len, STRCONST(".localhost")) || + tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + addr = get_localhost(port, hostname); + if(!addr) + result = CURLE_OUT_OF_MEMORY; + goto out; + } + +#ifndef CURL_DISABLE_DOH + if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + if(!async) { + async = hostip_async_new(data, dns_queries, hostname, port, + transport, for_proxy, timeout_ms); + if(!async) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + result = Curl_doh(data, async); + goto out; + } #else (void)allowDOH; #endif - DEBUGASSERT(dnscache); - if(!dnscache) { - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto error; + + /* Can we provide the requested IP specifics in resolving? */ + if(!can_resolve_dns_queries(data, dns_queries)) { + result = RESOLV_FAIL(for_proxy); + goto out; } +#ifdef CURLRES_ASYNCH + (void)addr; + if(!async) { + async = hostip_async_new(data, dns_queries, hostname, port, + transport, for_proxy, timeout_ms); + if(!async) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + result = Curl_async_getaddrinfo(data, async); + if(result == CURLE_AGAIN) { + /* the answer might be there already. Check. */ + CURLcode r2 = hostip_resolv_take_result(data, async, pdns); + if(r2) + result = r2; + else if(*pdns) + result = CURLE_OK; + } +#else + result = Curl_resolv_announce_start(data, NULL); + if(result) + goto out; + addr = Curl_sync_getaddrinfo(data, dns_queries, hostname, port, transport); + if(!addr) + result = RESOLV_FAIL(for_proxy); +#endif + +out: + if(!result) { + if(addr) { + /* we got a response, create a dns entry, add to cache, return */ + DEBUGASSERT(!*pdns); + *pdns = Curl_dnscache_mk_entry(data, dns_queries, &addr, hostname, port); + if(!*pdns) + result = CURLE_OUT_OF_MEMORY; + } + else if(!*pdns) + result = CURLE_AGAIN; + } + else if(*pdns) + Curl_dns_entry_unlink(data, pdns); + else if(addr) + Curl_freeaddrinfo(addr); + +#ifdef USE_CURL_ASYNC + if(async) { + if(result == CURLE_AGAIN) { /* still need it, link, return id. */ + *presolv_id = async->id; + async->next = data->state.async; + data->state.async = async; + } + else { + Curl_async_destroy(data, async); + } + } +#endif + return result; +} + +static CURLcode hostip_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + bool allowDOH, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) +{ + size_t hostname_len; + CURLcode result = RESOLV_FAIL(for_proxy); + bool cache_dns = FALSE; + + (void)timeout_ms; /* not used in all ifdefs */ + *presolv_id = 0; + *pdns = NULL; + +#ifdef CURL_DISABLE_DOH + (void)allowDOH; +#endif + /* We should intentionally error and not resolve .onion TLDs */ hostname_len = strlen(hostname); DEBUGASSERT(hostname_len); @@ -891,152 +700,88 @@ (curl_strequal(&hostname[hostname_len - 6], ".onion") || curl_strequal(&hostname[hostname_len - 7], ".onion."))) { failf(data, "Not resolving .onion address (RFC 7686)"); - goto error; + goto out; } +#ifdef DEBUGBUILD + CURL_TRC_DNS(data, "hostip_resolv(%s:%u, queries=%s)", + hostname, port, Curl_resolv_query_str(dns_queries)); + if((CURL_DNSQ_IP(dns_queries) == CURL_DNSQ_AAAA) && + getenv("CURL_DBG_RESOLV_FAIL_IPV6")) { + infof(data, "DEBUG fail ipv6 resolve"); + result = hostip_resolv_failed(data, hostname, for_proxy); + goto out; + } +#endif /* Let's check our DNS cache first */ - dnscache_lock(data, dnscache); - dns = fetch_addr(data, dnscache, hostname, port, ip_version); - if(dns) - dns->refcount++; /* we pass out the reference. */ - dnscache_unlock(data, dnscache); - if(dns) { + result = Curl_dnscache_get(data, dns_queries, hostname, port, pdns); + if(*pdns) { infof(data, "Hostname %s was found in DNS cache", hostname); result = CURLE_OK; - goto out; } - - /* No luck, we need to resolve hostname. Notify user callback. */ - if(data->set.resolver_start) { - void *resolver = NULL; - int st; -#ifdef CURLRES_ASYNCH - result = Curl_async_get_impl(data, &resolver); - if(result) - goto error; -#endif - Curl_set_in_callback(data, TRUE); - st = data->set.resolver_start(resolver, NULL, - data->set.resolver_start_client); - Curl_set_in_callback(data, FALSE); - if(st) { - result = CURLE_ABORTED_BY_CALLBACK; - goto error; - } + else if(result) { + infof(data, "Negative DNS entry"); + result = hostip_resolv_failed(data, hostname, for_proxy); } - - if(Curl_is_ipaddr(hostname)) { -#ifndef USE_RESOLVE_ON_IPS - /* shortcut literal IP addresses, if we are not told to resolve them. */ - result = Curl_str2addr(hostname, port, &addr); - if(result) - goto error; - goto out; -#endif - } - - if(curl_strequal(hostname, "localhost") || - curl_strequal(hostname, "localhost.") || - tailmatch(hostname, hostname_len, STRCONST(".localhost")) || - tailmatch(hostname, hostname_len, STRCONST(".localhost."))) { - addr = get_localhost(port, hostname); - result = addr ? CURLE_OK : CURLE_OUT_OF_MEMORY; - } -#ifndef CURL_DISABLE_DOH - else if(!Curl_is_ipaddr(hostname) && allowDOH && data->set.doh) { - result = Curl_doh(data, hostname, port, ip_version); - respwait = TRUE; - } -#endif else { - /* Can we provide the requested IP specifics in resolving? */ - if(!can_resolve_ip_version(data, ip_version)) { - result = CURLE_COULDNT_RESOLVE_HOST; - goto error; - } - -#ifdef CURLRES_ASYNCH - result = Curl_async_getaddrinfo(data, hostname, port, ip_version); - respwait = TRUE; -#else - respwait = FALSE; /* no async waiting here */ - addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version); - if(addr) - result = CURLE_OK; -#endif + /* No luck, we need to start resolving. */ + cache_dns = TRUE; + result = hostip_resolv_start(data, dns_queries, hostname, port, + transport, for_proxy, timeout_ms, allowDOH, + presolv_id, pdns); } out: - /* We either have found a `dns` or looked up the `addr` or `respwait` is set - * for an async operation. Everything else is a failure to resolve. */ - if(result) - ; - else if(dns) { - if(!dns->addr) { - infof(data, "Negative DNS entry"); - dns->refcount--; - return CURLE_COULDNT_RESOLVE_HOST; + if(result && (result != CURLE_AGAIN)) { + Curl_dns_entry_unlink(data, pdns); + if(IS_RESOLV_FAIL(result)) { + if(cache_dns) + Curl_dnscache_add_negative(data, dns_queries, hostname, port); + failf(data, "Could not resolve: %s:%u", hostname, port); } - *entry = dns; - return CURLE_OK; - } - else if(addr) { - /* we got a response, create a dns entry, add to cache, return */ - dns = Curl_dnscache_mk_entry(data, &addr, hostname, 0, port, FALSE); - if(!dns || Curl_dnscache_add(data, dns)) { - /* this is OOM or similar, do not store such negative resolves */ - result = CURLE_OUT_OF_MEMORY; - goto error; + else { + failf(data, "Error %d resolving %s:%u", result, hostname, port); } - show_resolve_info(data, dns); - *entry = dns; - return CURLE_OK; } - else if(respwait) { -#ifdef USE_CURL_ASYNC - if(!Curl_resolv_check(data, &dns)) { - *entry = dns; - return dns ? CURLE_OK : CURLE_AGAIN; - } -#endif - result = CURLE_COULDNT_RESOLVE_HOST; + else if(cache_dns && *pdns) { + result = Curl_dnscache_add(data, *pdns); + if(result) + Curl_dns_entry_unlink(data, pdns); } -error: - if(dns) - Curl_resolv_unlink(data, &dns); - Curl_async_shutdown(data); - if(result == CURLE_COULDNT_RESOLVE_HOST) - store_negative_resolve(data, hostname, port); - DEBUGASSERT(result); + return result; } CURLcode Curl_resolv_blocking(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **entry) + uint16_t port, + uint8_t transport, + struct Curl_dns_entry **pdns) { CURLcode result; + uint32_t resolv_id; DEBUGASSERT(hostname && *hostname); - *entry = NULL; - result = Curl_resolv(data, hostname, port, ip_version, FALSE, entry); + *pdns = NULL; + /* We cannot do a blocking resolve using DoH currently */ + result = hostip_resolv(data, dns_queries, + hostname, port, transport, FALSE, 0, FALSE, + &resolv_id, pdns); switch(result) { case CURLE_OK: - DEBUGASSERT(*entry); - return CURLE_OK; + DEBUGASSERT(*pdns); + break; +#ifdef USE_CURL_ASYNC case CURLE_AGAIN: - DEBUGASSERT(!*entry); - result = Curl_async_await(data, entry); - if(result || !*entry) { - /* close the connection, since we cannot return failure here without - cleaning up this connection properly. */ - connclose(data->conn, "async resolve failed"); - } - return result; + DEBUGASSERT(!*pdns); + result = Curl_async_await(data, resolv_id, pdns); + Curl_resolv_destroy(data, resolv_id); + break; +#endif default: - return result; + break; } + return result; } #ifdef USE_ALARM_TIMEOUT @@ -1052,35 +797,18 @@ } #endif /* USE_ALARM_TIMEOUT */ -/* - * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a - * timeout. This function might return immediately if we are using asynch - * resolves. See the return codes. - * - * The cache entry we return will get its 'inuse' counter increased when this - * function is used. You MUST call Curl_resolv_unlink() later (when you are - * done using this struct) to decrease the reference counter again. - * - * If built with a synchronous resolver and use of signals is not - * disabled by the application, then a nonzero timeout will cause a - * timeout after the specified number of milliseconds. Otherwise, timeout - * is ignored. - * - * Return codes: - * CURLE_OK = success, *entry set to non-NULL - * CURLE_AGAIN = resolving in progress, *entry == NULL - * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL - * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL - */ - -CURLcode Curl_resolv_timeout(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **entry, - timediff_t timeoutms) -{ #ifdef USE_ALARM_TIMEOUT + +static CURLcode resolv_alarm_timeout(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **entry) +{ #ifdef HAVE_SIGACTION struct sigaction keep_sigact; /* store the old struct here */ volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */ @@ -1092,32 +820,17 @@ #endif /* HAVE_SIGACTION */ volatile long timeout; volatile unsigned int prev_alarm = 0; -#endif /* USE_ALARM_TIMEOUT */ CURLcode result; DEBUGASSERT(hostname && *hostname); - *entry = NULL; - - if(timeoutms < 0) - /* got an already expired timeout */ - return CURLE_OPERATION_TIMEDOUT; - -#ifdef USE_ALARM_TIMEOUT - if(data->set.no_signal) - /* Ignore the timeout when signals are disabled */ - timeout = 0; - else - timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms; - - if(!timeout + DEBUGASSERT(timeout_ms > 0); + DEBUGASSERT(!data->set.no_signal); #ifndef CURL_DISABLE_DOH - || data->set.doh + DEBUGASSERT(!data->set.doh); #endif - ) - /* USE_ALARM_TIMEOUT defined, but no timeout actually requested or resolve - done using DoH */ - return Curl_resolv(data, hostname, port, ip_version, TRUE, entry); + *entry = NULL; + timeout = (timeout_ms > LONG_MAX) ? LONG_MAX : (long)timeout_ms; if(timeout < 1000) { /* The alarm() function only provides integer second resolution, so if we want to wait less than one second we must bail out already now. */ @@ -1168,23 +881,12 @@ prev_alarm = alarm(curlx_sltoui(timeout / 1000L)); } -#else /* !USE_ALARM_TIMEOUT */ -#ifndef CURLRES_ASYNCH - if(timeoutms) - infof(data, "timeout on name lookup is not supported"); -#else - (void)timeoutms; -#endif -#endif /* USE_ALARM_TIMEOUT */ - /* Perform the actual name resolution. This might be interrupted by an - * alarm if it takes too long. - */ - result = Curl_resolv(data, hostname, port, ip_version, TRUE, entry); + * alarm if it takes too long. */ + result = hostip_resolv(data, dns_queries, hostname, port, transport, + for_proxy, timeout_ms, FALSE, presolv_id, entry); -#ifdef USE_ALARM_TIMEOUT clean_up: - if(!prev_alarm) /* deactivate a possibly active alarm before uninstalling the handler */ alarm(0); @@ -1227,364 +929,202 @@ else alarm((unsigned int)alarm_set); } -#endif /* USE_ALARM_TIMEOUT */ return result; } +#endif /* USE_ALARM_TIMEOUT */ + /* - * Curl_resolv_unlink() releases a reference to the given cached DNS entry. - * When the reference count reaches 0, the entry is destroyed. It is important - * that only one unlink is made for each Curl_resolv() call. + * Curl_resolv() is the main name resolve function within libcurl. It resolves + * a name and returns a pointer to the entry in the 'entry' argument. This + * function might return immediately if we are using asynch resolves. See the + * return codes. * - * May be called with 'data' == NULL for global cache. + * The cache entry we return will get its 'inuse' counter increased when this + * function is used. You MUST call Curl_dns_entry_unlink() later (when you are + * done using this struct) to decrease the reference counter again. + * + * If built with a synchronous resolver and use of signals is not + * disabled by the application, then a nonzero timeout will cause a + * timeout after the specified number of milliseconds. Otherwise, timeout + * is ignored. + * + * Return codes: + * CURLE_OK = success, *pdns set to non-NULL + * CURLE_AGAIN = resolving in progress, *pdns == NULL + * any other CURLcode error, *pdns == NULL */ -void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns) +CURLcode Curl_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns) { - if(*pdns) { - struct Curl_dnscache *dnscache = dnscache_get(data); - struct Curl_dns_entry *dns = *pdns; - *pdns = NULL; - dnscache_lock(data, dnscache); - dns->refcount--; - if(dns->refcount == 0) - dnscache_entry_free(dns); - dnscache_unlock(data, dnscache); + DEBUGASSERT(hostname && *hostname); + *presolv_id = 0; + *pdns = NULL; + + if(timeout_ms < 0) + /* got an already expired timeout */ + return CURLE_OPERATION_TIMEDOUT; + else if(!timeout_ms) + timeout_ms = CURL_TIMEOUT_RESOLVE_MS; + +#ifdef USE_ALARM_TIMEOUT + if(timeout_ms && data->set.no_signal) { + /* Cannot use ALARM when signals are disabled */ + timeout_ms = 0; } -} + if(timeout_ms && !Curl_doh_wanted(data)) { + return resolv_alarm_timeout(data, dns_queries, hostname, port, transport, + for_proxy, timeout_ms, presolv_id, pdns); + } +#endif /* !USE_ALARM_TIMEOUT */ -static void dnscache_entry_dtor(void *entry) -{ - struct Curl_dns_entry *dns = (struct Curl_dns_entry *)entry; - DEBUGASSERT(dns && (dns->refcount > 0)); - dns->refcount--; - if(dns->refcount == 0) - dnscache_entry_free(dns); -} - -/* - * Curl_dnscache_init() inits a new DNS cache. - */ -void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size) -{ - Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare, - dnscache_entry_dtor); -} - -void Curl_dnscache_destroy(struct Curl_dnscache *dns) -{ - Curl_hash_destroy(&dns->entries); -} - -CURLcode Curl_loadhostpairs(struct Curl_easy *data) -{ - struct Curl_dnscache *dnscache = dnscache_get(data); - struct curl_slist *hostp; - - if(!dnscache) - return CURLE_FAILED_INIT; - - /* Default is no wildcard found */ - data->state.wildcard_resolve = FALSE; - - for(hostp = data->state.resolve; hostp; hostp = hostp->next) { - char entry_id[MAX_HOSTCACHE_LEN]; - const char *host = hostp->data; - struct Curl_str source; - if(!host) - continue; - if(*host == '-') { - curl_off_t num = 0; - size_t entry_len; - host++; - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']') || - curlx_str_single(&host, ':')) - continue; - } - else { - if(curlx_str_until(&host, &source, 4096, ':') || - curlx_str_single(&host, ':')) { - continue; - } - } - - if(!curlx_str_number(&host, &num, 0xffff)) { - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), - curlx_strlen(&source), (int)num, - entry_id, sizeof(entry_id)); - dnscache_lock(data, dnscache); - /* delete entry, ignore if it did not exist */ - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - dnscache_unlock(data, dnscache); - } - } - else { - struct Curl_dns_entry *dns; - struct Curl_addrinfo *head = NULL, *tail = NULL; - size_t entry_len; - char address[64]; - curl_off_t port = 0; - bool permanent = TRUE; - bool error = TRUE; - VERBOSE(const char *addresses = NULL); - - if(*host == '+') { - host++; - permanent = FALSE; - } - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']')) - continue; - } - else { - if(curlx_str_until(&host, &source, 4096, ':')) - continue; - } - if(curlx_str_single(&host, ':') || - curlx_str_number(&host, &port, 0xffff) || - curlx_str_single(&host, ':')) - goto err; - - VERBOSE(addresses = host); - - /* start the address section */ - while(*host) { - struct Curl_str target; - struct Curl_addrinfo *ai; - CURLcode result; - - if(!curlx_str_single(&host, '[')) { - if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') || - curlx_str_single(&host, ']')) - goto err; - } - else { - if(curlx_str_until(&host, &target, 4096, ',')) { - if(curlx_str_single(&host, ',')) - goto err; - /* survive nothing but a comma */ - continue; - } - } -#ifndef USE_IPV6 - if(memchr(curlx_str(&target), ':', curlx_strlen(&target))) { - infof(data, "Ignoring resolve address '%.*s', missing IPv6 support.", - (int)curlx_strlen(&target), curlx_str(&target)); - if(curlx_str_single(&host, ',')) - goto err; - continue; - } +#ifndef CURLRES_ASYNCH + if(timeout_ms) + infof(data, "timeout on name lookup is not supported"); #endif - if(curlx_strlen(&target) >= sizeof(address)) - goto err; - - memcpy(address, curlx_str(&target), curlx_strlen(&target)); - address[curlx_strlen(&target)] = '\0'; - - result = Curl_str2addr(address, (int)port, &ai); - if(result) { - infof(data, "Resolve address '%s' found illegal", address); - goto err; - } - - if(tail) { - tail->ai_next = ai; - tail = tail->ai_next; - } - else { - head = tail = ai; - } - if(curlx_str_single(&host, ',')) - break; - } - - if(!head) - goto err; - - error = FALSE; -err: - if(error) { - failf(data, "Could not parse CURLOPT_RESOLVE entry '%s'", hostp->data); - Curl_freeaddrinfo(head); - return CURLE_SETOPT_OPTION_SYNTAX; - } - - /* Create an entry id, based upon the hostname and port */ - entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source), - (int)port, - entry_id, sizeof(entry_id)); - - dnscache_lock(data, dnscache); - - /* See if it is already in our dns cache */ - dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1); - - if(dns) { - infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T - " - old addresses discarded", (int)curlx_strlen(&source), - curlx_str(&source), port); - /* delete old entry, there are two reasons for this - 1. old entry may have different addresses. - 2. even if entry with correct addresses is already in the cache, - but if it is close to expire, then by the time next http - request is made, it can get expired and pruned because old - entry is not necessarily marked as permanent. - 3. when adding a non-permanent entry, we want it to remove and - replace an existing permanent entry. - 4. when adding a non-permanent entry, we want it to get a "fresh" - timeout that starts _now_. */ - - Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1); - } - - /* put this new host in the cache */ - dns = dnscache_add_addr(data, dnscache, &head, curlx_str(&source), - curlx_strlen(&source), (int)port, permanent); - if(dns) - /* release the returned reference; the cache itself will keep the - * entry alive: */ - dns->refcount--; - - dnscache_unlock(data, dnscache); - - if(!dns) - return CURLE_OUT_OF_MEMORY; - - infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s", - (int)curlx_strlen(&source), curlx_str(&source), port, addresses, - permanent ? "" : " (non-permanent)"); - - /* Wildcard hostname */ - if(curlx_str_casecompare(&source, "*")) { - infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard", - port); - data->state.wildcard_resolve = TRUE; - } - } - } - data->state.resolve = NULL; /* dealt with now */ - - return CURLE_OK; + return hostip_resolv(data, dns_queries, hostname, port, transport, + for_proxy, timeout_ms, TRUE, presolv_id, pdns); } #ifdef USE_CURL_ASYNC -CURLcode Curl_resolv_check(struct Curl_easy *data, - struct Curl_dns_entry **dns) + +struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data, + uint32_t resolv_id) { + struct Curl_resolv_async *async = data->state.async; + for(; async; async = async->next) { + if(async->id == resolv_id) + return async; + } + return NULL; +} + +CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns) +{ + struct Curl_resolv_async *async = Curl_async_get(data, resolv_id); CURLcode result; /* If async resolving is ongoing, this must be set */ - if(!data->state.async.hostname) + if(!async) return CURLE_FAILED_INIT; /* check if we have the name resolved by now (from someone else) */ - *dns = Curl_dnscache_get(data, data->state.async.hostname, - data->state.async.port, - data->state.async.ip_version); - if(*dns) { + result = Curl_dnscache_get(data, async->dns_queries, + async->hostname, async->port, pdns); + if(*pdns) { /* Tell a possibly async resolver we no longer need the results. */ - infof(data, "Hostname '%s' was found in DNS cache", - data->state.async.hostname); - Curl_async_shutdown(data); - data->state.async.dns = *dns; - data->state.async.done = TRUE; + infof(data, "Hostname '%s' was found in DNS cache", async->hostname); + Curl_async_shutdown(data, async); return CURLE_OK; } - -#ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) { - result = Curl_doh_is_resolved(data, dns); - if(result) - Curl_resolver_error(data, NULL); + else if(result) { + Curl_async_shutdown(data, async); + return Curl_async_failed(data, async, NULL); } - else -#endif - result = Curl_async_is_resolved(data, dns); - if(*dns) - show_resolve_info(data, *dns); - if((result == CURLE_COULDNT_RESOLVE_HOST) || - (result == CURLE_COULDNT_RESOLVE_PROXY)) - store_negative_resolve(data, data->state.async.hostname, - data->state.async.port); + + result = hostip_resolv_take_result(data, async, pdns); + + if(*pdns) { + /* Add to cache */ + result = Curl_dnscache_add(data, *pdns); + if(result) + Curl_dns_entry_unlink(data, pdns); + } + else if(IS_RESOLV_FAIL(result)) { + Curl_dnscache_add_negative(data, async->dns_queries, + async->hostname, async->port); + failf(data, "Could not resolve: %s:%u", async->hostname, async->port); + } + else if(result) { + failf(data, "Error %d resolving %s:%u", + result, async->hostname, async->port); + } return result; } -#endif CURLcode Curl_resolv_pollset(struct Curl_easy *data, struct easy_pollset *ps) { -#ifdef CURLRES_ASYNCH -#ifndef CURL_DISABLE_DOH - if(data->conn->bits.doh) - /* nothing to wait for during DoH resolve, those handles have their own - sockets */ - return CURLE_OK; -#endif - return Curl_async_pollset(data, ps); -#else - (void)data; + struct Curl_resolv_async *async = data->state.async; + CURLcode result = CURLE_OK; + (void)ps; - return CURLE_OK; + for(; async && !result; async = async->next) { +#ifndef CURL_DISABLE_DOH + if(async->doh) /* DoH has nothing for the pollset */ + continue; #endif -} - -/* Call this function after Curl_connect() has returned async=TRUE and - then a successful name resolve has been received. - - Note: this function disconnects and frees the conn data in case of - resolve failure */ -CURLcode Curl_once_resolved(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_done) -{ - CURLcode result; - struct connectdata *conn = data->conn; - -#ifdef USE_CURL_ASYNC - if(data->state.async.dns) { - DEBUGASSERT(data->state.async.dns == dns); - data->state.async.dns = NULL; - } -#endif - - result = Curl_setup_conn(data, dns, protocol_done); - - if(result) { - Curl_detach_connection(data); - Curl_conn_terminate(data, conn, TRUE); + result = Curl_async_pollset(data, async, ps); } return result; } -/* - * Curl_resolver_error() calls failf() with the appropriate message after a - * resolve error - */ - -#ifdef USE_CURL_ASYNC -CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail) +void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id) { - struct connectdata *conn = data->conn; - const char *host_or_proxy = "host"; - const char *name = conn->host.dispname; - CURLcode result = CURLE_COULDNT_RESOLVE_HOST; + struct Curl_resolv_async **panchor = &data->state.async; -#ifndef CURL_DISABLE_PROXY - if(conn->bits.proxy) { - host_or_proxy = "proxy"; - result = CURLE_COULDNT_RESOLVE_PROXY; - name = conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : - conn->http_proxy.host.dispname; + for(; *panchor; panchor = &(*panchor)->next) { + struct Curl_resolv_async *async = *panchor; + if(async->id == resolv_id) { + *panchor = async->next; + Curl_async_destroy(data, async); + break; + } } -#endif - - failf(data, "Could not resolve %s: %s%s%s%s", host_or_proxy, name, - detail ? " (" : "", detail ? detail : "", detail ? ")" : ""); - return result; } + +void Curl_resolv_shutdown_all(struct Curl_easy *data) +{ + struct Curl_resolv_async *async = data->state.async; + for(; async; async = async->next) { + Curl_async_shutdown(data, async); + } +} + +void Curl_resolv_destroy_all(struct Curl_easy *data) +{ + while(data->state.async) { + struct Curl_resolv_async *async = data->state.async; + data->state.async = async->next; + Curl_async_destroy(data, async); + } +} + #endif /* USE_CURL_ASYNC */ + +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_resolv_unix(struct Curl_easy *data, + const char *unix_path, + bool abstract_path, + struct Curl_dns_entry **pdns) +{ + struct Curl_addrinfo *addr; + CURLcode result; + + DEBUGASSERT(unix_path); + *pdns = NULL; + + result = Curl_unix2addr(unix_path, abstract_path, &addr); + if(result) { + if(result == CURLE_TOO_LARGE) { + /* Long paths are not supported for now */ + failf(data, "Unix socket path too long: '%s'", unix_path); + result = CURLE_COULDNT_RESOLVE_HOST; + } + return result; + } + + *pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0); + return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY; +} +#endif /* USE_UNIX_SOCKETS */
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h index 6c7f129..780fb4d 100644 --- a/Utilities/cmcurl/lib/hostip.h +++ b/Utilities/cmcurl/lib/hostip.h
@@ -35,8 +35,7 @@ */ #define CURL_HOSTENT_SIZE 9000 -#define CURL_TIMEOUT_RESOLVE 300 /* when using asynch methods, we allow this - many seconds for a name resolve */ +#define CURL_TIMEOUT_RESOLVE_MS (300 * 1000) struct addrinfo; struct hostent; @@ -45,6 +44,22 @@ struct easy_pollset; struct Curl_https_rrinfo; struct Curl_multi; +struct Curl_dns_entry; + +/* DNS query types */ +#define CURL_DNSQ_A (1U << 0) +#define CURL_DNSQ_AAAA (1U << 1) +#define CURL_DNSQ_HTTPS (1U << 2) + +#define CURL_DNSQ_ALL (CURL_DNSQ_A | CURL_DNSQ_AAAA | CURL_DNSQ_HTTPS) +#define CURL_DNSQ_IP(x) (uint8_t)((x)&(CURL_DNSQ_A | CURL_DNSQ_AAAA)) + +#ifdef CURLVERBOSE +const char *Curl_resolv_query_str(uint8_t dns_queries); +#endif + +/* Return CURL_DNSQ_* bits for the transfer and ip_version. */ +uint8_t Curl_resolv_dns_queries(struct Curl_easy *data, uint8_t ip_version); enum alpnid { ALPN_none = 0, @@ -53,151 +68,108 @@ ALPN_h3 = CURLALTSVC_H3 }; -struct Curl_dns_entry { - struct Curl_addrinfo *addr; -#ifdef USE_HTTPSRR - struct Curl_https_rrinfo *hinfo; -#endif - /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */ - struct curltime timestamp; - /* reference counter, entry is freed on reaching 0 */ - size_t refcount; - /* hostname port number that resolved to addr. */ - int hostport; - /* hostname that resolved to addr. may be NULL (Unix domain sockets). */ - char hostname[1]; -}; - -struct Curl_dnscache { - struct Curl_hash entries; -}; - bool Curl_host_is_ipnum(const char *hostname); -/* - * Curl_resolv() returns an entry with the info for the specified host - * and port. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -CURLcode Curl_resolv(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - bool allowDOH, - struct Curl_dns_entry **entry); - -CURLcode Curl_resolv_blocking(struct Curl_easy *data, - const char *hostname, - int port, - int ip_version, - struct Curl_dns_entry **entry); - -CURLcode Curl_resolv_timeout(struct Curl_easy *data, - const char *hostname, int port, - int ip_version, - struct Curl_dns_entry **entry, - timediff_t timeoutms); - #ifdef USE_IPV6 - /* probe if it seems to work */ CURLcode Curl_probeipv6(struct Curl_multi *multi); -/* - * Curl_ipv6works() returns TRUE if IPv6 seems to work. - */ -bool Curl_ipv6works(struct Curl_easy *data); #else #define Curl_probeipv6(x) CURLE_OK -#define Curl_ipv6works(x) FALSE #endif -/* unlink a dns entry, potentially shared with a cache */ -void Curl_resolv_unlink(struct Curl_easy *data, - struct Curl_dns_entry **pdns); - -/* init a new dns cache */ -void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size); - -void Curl_dnscache_destroy(struct Curl_dnscache *dns); - -/* prune old entries from the DNS cache */ -void Curl_dnscache_prune(struct Curl_easy *data); - -/* clear the DNS cache */ -void Curl_dnscache_clear(struct Curl_easy *data); - /* IPv4 thread-safe resolve function used for synch and asynch builds */ -struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port); - -CURLcode Curl_once_resolved(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_done); +struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, uint16_t port); /* * Curl_printable_address() returns a printable version of the 1st address - * given in the 'ip' argument. The result will be stored in the buf that is + * given in the 'ai' argument. The result will be stored in the buf that is * bufsize bytes big. */ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, size_t bufsize); -/* - * Make a `Curl_dns_entry`. - * Creates a dnscache entry *without* adding it to a dnscache. This allows - * further modifications of the entry *before* then adding it to a cache. - * - * The entry is created with a reference count of 1. - * Use `Curl_resolv_unlink()` to release your hold on it. - * - * The call takes ownership of `addr`, even in case of failure, and always - * clears `*paddr`. It makes a copy of `hostname`. - * - * Returns entry or NULL on OOM. +/* Start DNS resolving for the given parameters. Returns + * - CURLE_OK: `*pdns` is the resolved DNS entry (needs to be unlinked). + * `*presolv_id` is 0. + * - CURLE_AGAIN: resolve is asynchronous and not finished yet. + * `presolv_id` is the identifier for querying results later. + * - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0. */ -struct Curl_dns_entry * -Curl_dnscache_mk_entry(struct Curl_easy *data, - struct Curl_addrinfo **paddr, - const char *hostname, - size_t hostlen, /* length or zero */ - int port, - bool permanent); +CURLcode Curl_resolv(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + bool for_proxy, + timediff_t timeout_ms, + uint32_t *presolv_id, + struct Curl_dns_entry **pdns); -/* - * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache. - * - * Returns the Curl_dns_entry entry pointer or NULL if not in the cache. - * - * The returned data *MUST* be "released" with Curl_resolv_unlink() after - * use, or we will leak memory! - */ -struct Curl_dns_entry *Curl_dnscache_get(struct Curl_easy *data, - const char *hostname, int port, - int ip_version); +CURLcode Curl_resolv_blocking(struct Curl_easy *data, + uint8_t dns_queries, + const char *hostname, + uint16_t port, + uint8_t transport, + struct Curl_dns_entry **pdns); -/* - * Curl_dnscache_addr() adds `entry` to the cache, increasing its - * reference count on success. - */ -CURLcode Curl_dnscache_add(struct Curl_easy *data, - struct Curl_dns_entry *entry); - -/* - * Populate the cache with specified entries from CURLOPT_RESOLVE. - */ -CURLcode Curl_loadhostpairs(struct Curl_easy *data); +/* Announce start of a resolve operation to application callback, + * passing the resolver implementation (maybe NULL). */ +CURLcode Curl_resolv_announce_start(struct Curl_easy *data, + void *resolver); #ifdef USE_CURL_ASYNC -CURLcode Curl_resolv_check(struct Curl_easy *data, - struct Curl_dns_entry **dns); -#else -#define Curl_resolv_check(x, y) CURLE_NOT_BUILT_IN -#endif + CURLcode Curl_resolv_pollset(struct Curl_easy *data, struct easy_pollset *ps); -CURLcode Curl_resolver_error(struct Curl_easy *data, const char *detail); +/* Get the `async` struct for the given `resolv_id`, if it exists. */ +struct Curl_resolv_async *Curl_async_get(struct Curl_easy *data, + uint32_t resolv_id); + +/* Shut down all resolves of the given easy handle. */ +void Curl_resolv_shutdown_all(struct Curl_easy *data); + +/* Destroy all resolve resources of the given easy handle. */ +void Curl_resolv_destroy_all(struct Curl_easy *data); + +CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id, + struct Curl_dns_entry **pdns); + +void Curl_resolv_destroy(struct Curl_easy *data, uint32_t resolv_id); + +/* How much time has gone by since start of resolve. + * Returns CURL_TIMEOUT_RESOLVE_MS if `resolv_id` is no longer valid. */ +timediff_t Curl_resolv_elapsed_ms(struct Curl_easy *data, + uint32_t resolv_id); + +/* Return TRUE if `resolv_id` has answers (positive or negative) to + * all queries in `dns_queries`. + * Queries not requested are considered answered. */ +bool Curl_resolv_has_answers(struct Curl_easy *data, + uint32_t resolv_id, uint8_t dns_queries); + +const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data, + uint32_t resolv_id, + int ai_family, + unsigned int index); +#ifdef USE_HTTPSRR +const struct Curl_https_rrinfo *Curl_resolv_get_https(struct Curl_easy *data, + uint32_t resolv_id); +bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id); +#endif /* USE_HTTPSRR */ + +#else /* !USE_CURL_ASYNC */ +#define Curl_resolv_shutdown_all(x) Curl_nop_stmt +#define Curl_resolv_destroy_all(x) Curl_nop_stmt +#define Curl_resolv_take_result(x, y, z) CURLE_NOT_BUILT_IN +#define Curl_resolv_elapsed_ms(x, y) CURL_TIMEOUT_RESOLVE_MS +#define Curl_resolv_has_answers(x, y, z) TRUE +#define Curl_resolv_get_ai(x, y, z, a) NULL +#define Curl_resolv_get_https(x, y) NULL +#define Curl_resolv_knows_https(x, y) TRUE +#define Curl_resolv_pollset(x, y) CURLE_OK +#define Curl_resolv_destroy(x, y) Curl_nop_stmt +#endif /* USE_CURL_ASYNC */ #ifdef CURLRES_SYNCH /* @@ -206,10 +178,17 @@ * support and platform. */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version); + uint16_t port, + uint8_t transport); +#endif +#ifdef USE_UNIX_SOCKETS +CURLcode Curl_resolv_unix(struct Curl_easy *data, + const char *unix_path, + bool abstract_path, + struct Curl_dns_entry **pdns); #endif #endif /* HEADER_CURL_HOSTIP_H */
diff --git a/Utilities/cmcurl/lib/hostip4.c b/Utilities/cmcurl/lib/hostip4.c index f8cfac1..fb35e39 100644 --- a/Utilities/cmcurl/lib/hostip4.c +++ b/Utilities/cmcurl/lib/hostip4.c
@@ -68,13 +68,15 @@ * */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version) + uint16_t port, + uint8_t transport) { struct Curl_addrinfo *ai = NULL; - (void)ip_version; + (void)dns_queries; + (void)transport; ai = Curl_ipv4_resolve_r(hostname, port); if(!ai) @@ -85,7 +87,8 @@ #endif /* CURLRES_SYNCH */ #endif /* CURLRES_IPV4 */ -#if defined(CURLRES_IPV4) && !defined(CURLRES_ARES) && !defined(CURLRES_AMIGA) +#if defined(CURLRES_IPV4) && !defined(USE_RESOLV_ARES) && \ + !defined(CURLRES_AMIGA) /* * Curl_ipv4_resolve_r() - ipv4 thread-safe resolver function. @@ -95,7 +98,7 @@ * */ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, - int port) + uint16_t port) { #if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE)) && \ defined(HAVE_GETHOSTBYNAME_R_3) @@ -277,4 +280,4 @@ return ai; } -#endif /* CURLRES_IPV4 && !CURLRES_ARES && !CURLRES_AMIGA */ +#endif /* CURLRES_IPV4 && !USE_RESOLV_ARES && !CURLRES_AMIGA */
diff --git a/Utilities/cmcurl/lib/hostip6.c b/Utilities/cmcurl/lib/hostip6.c index 8aa1974..7412f42 100644 --- a/Utilities/cmcurl/lib/hostip6.c +++ b/Utilities/cmcurl/lib/hostip6.c
@@ -63,9 +63,10 @@ * Curl_freeaddrinfo(), nothing else. */ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, + uint8_t dns_queries, const char *hostname, - int port, - int ip_version) + uint16_t port, + uint8_t transport) { struct addrinfo hints; struct Curl_addrinfo *res; @@ -77,14 +78,12 @@ #endif int pf = PF_INET; - if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) - /* The stack seems to be IPv6-enabled */ + if(dns_queries & CURL_DNSQ_AAAA) pf = PF_UNSPEC; memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = - (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? + hints.ai_socktype = (transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; #ifndef USE_RESOLVE_ON_IPS
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c index 03f51f8..400b442 100644 --- a/Utilities/cmcurl/lib/hsts.c +++ b/Utilities/cmcurl/lib/hsts.c
@@ -40,7 +40,7 @@ #define MAX_HSTS_LINE 4095 #define MAX_HSTS_HOSTLEN 2048 -#define MAX_HSTS_DATELEN 256 +#define MAX_HSTS_DATELEN 17 #define UNLIMITED "unlimited" #if defined(DEBUGBUILD) || defined(UNITTESTS) @@ -91,6 +91,20 @@ } } +/* append the new entry to the list after possibly removing an old entry + first */ +static void hsts_append(struct hsts *h, struct stsentry *sts) +{ + if(Curl_llist_count(&h->list) == MAX_HSTS_ENTRIES) { + /* It's full. Remove the first entry in the list */ + struct Curl_llist_node *e = Curl_llist_head(&h->list); + struct stsentry *oldsts = Curl_node_elem(e); + Curl_node_remove(e); + hsts_free(oldsts); + } + Curl_llist_append(&h->list, sts, &sts->node); +} + static CURLcode hsts_create(struct hsts *h, const char *hostname, size_t hlen, @@ -111,7 +125,7 @@ memcpy(sts->host, hostname, hlen); sts->expires = expires; sts->includeSubDomains = subdomains; - Curl_llist_append(&h->list, sts, &sts->node); + hsts_append(h, sts); } return CURLE_OK; } @@ -386,6 +400,61 @@ } /* only returns SERIOUS errors */ +static CURLcode hsts_add_host_expire(struct hsts *h, + const char *host, size_t hostlen, + const char *expire, size_t explen, + bool subdomain) /* default */ +{ + CURLcode result = CURLE_OK; + struct stsentry *e; + char dbuf[MAX_HSTS_DATELEN + 1]; + time_t expires = 0; + time_t now = time(NULL); + + /* The date parser works on a null-terminated string. */ + if(explen > MAX_HSTS_DATELEN) + return CURLE_BAD_FUNCTION_ARGUMENT; + memcpy(dbuf, expire, explen); + dbuf[explen] = 0; + + if(!strcmp(dbuf, UNLIMITED)) + expires = TIME_T_MAX; + else + Curl_getdate_capped(dbuf, &expires); + + if(expires <= now) + /* this entry already expired */ + return CURLE_OK; + + if(host[0] == '.') { + host++; + hostlen--; + subdomain = TRUE; + } + if(hostlen && (host[hostlen - 1] == '.')) + /* strip off any trailing dot */ + hostlen--; + + if(hostlen) { + /* only add it if not already present */ + e = Curl_hsts(h, host, hostlen, subdomain); + if(!e) + result = hsts_create(h, host, hostlen, subdomain, expires); + /* 'host' is not necessarily null terminated */ + else if((hostlen == strlen(e->host) && + curl_strnequal(host, e->host, hostlen))) { + /* the same hostname, use the largest expire time and keep the strictest + subdomain policy */ + if(expires > e->expires) + e->expires = expires; + if(subdomain) + e->includeSubDomains = TRUE; + } + } + return result; +} + +/* only returns SERIOUS errors */ static CURLcode hsts_add(struct hsts *h, const char *line) { /* Example lines: @@ -401,39 +470,9 @@ curlx_str_newline(&line)) ; else { - CURLcode result = CURLE_OK; - bool subdomain = FALSE; - struct stsentry *e; - char dbuf[MAX_HSTS_DATELEN + 1]; - time_t expires = 0; - const char *hp = curlx_str(&host); - - /* The date parser works on a null-terminated string. The maximum length - is upheld by curlx_str_quotedword(). */ - memcpy(dbuf, curlx_str(&date), curlx_strlen(&date)); - dbuf[curlx_strlen(&date)] = 0; - - if(!strcmp(dbuf, UNLIMITED)) - expires = TIME_T_MAX; - else - Curl_getdate_capped(dbuf, &expires); - - if(hp[0] == '.') { - curlx_str_nudge(&host, 1); - subdomain = TRUE; - } - /* only add it if not already present */ - e = Curl_hsts(h, curlx_str(&host), curlx_strlen(&host), subdomain); - if(!e) - result = hsts_create(h, curlx_str(&host), curlx_strlen(&host), - subdomain, expires); - else if(curlx_str_casecompare(&host, e->host)) { - /* the same hostname, use the largest expire time */ - if(expires > e->expires) - e->expires = expires; - } - if(result) - return result; + return hsts_add_host_expire(h, curlx_str(&host), curlx_strlen(&host), + curlx_str(&date), curlx_strlen(&date), + FALSE); } return CURLE_OK; @@ -456,23 +495,23 @@ e.namelen = sizeof(buffer) - 1; e.includeSubDomains = FALSE; /* default */ e.expire[0] = 0; + e.expire[MAX_HSTS_DATELEN] = 0; e.name[0] = 0; /* to make it clean */ + e.name[MAX_HSTS_HOSTLEN] = 0; sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp); if(sc == CURLSTS_OK) { - time_t expires = 0; CURLcode result; - DEBUGASSERT(e.name[0]); - if(!e.name[0]) - /* bail out if no name was stored */ + const char *date = e.expire; + if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] || + e.name[MAX_HSTS_HOSTLEN]) + /* bail out if no name was stored or if a null terminator is gone */ return CURLE_BAD_FUNCTION_ARGUMENT; - if(e.expire[0]) - Curl_getdate_capped(e.expire, &expires); - else - expires = TIME_T_MAX; /* the end of time */ - result = hsts_create(h, e.name, strlen(e.name), - /* bitfield to bool conversion: */ - e.includeSubDomains ? TRUE : FALSE, - expires); + if(!date[0]) + date = UNLIMITED; + result = hsts_add_host_expire(h, e.name, strlen(e.name), + date, strlen(date), + /* bitfield to bool conversion: */ + e.includeSubDomains ? TRUE : FALSE); if(result) return result; } @@ -505,26 +544,28 @@ fp = curlx_fopen(file, FOPEN_READTEXT); if(fp) { - struct dynbuf buf; - bool eof = FALSE; - curlx_dyn_init(&buf, MAX_HSTS_LINE); - do { - result = Curl_get_line(&buf, fp, &eof); - if(!result) { - const char *lineptr = curlx_dyn_ptr(&buf); - curlx_str_passblanks(&lineptr); + curlx_struct_stat stat; + if((curlx_fstat(fileno(fp), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + struct dynbuf buf; + bool eof = FALSE; + curlx_dyn_init(&buf, MAX_HSTS_LINE); + do { + result = Curl_get_line(&buf, fp, &eof); + if(!result) { + const char *lineptr = curlx_dyn_ptr(&buf); + curlx_str_passblanks(&lineptr); - /* - * Skip empty or commented lines, since we know the line will have a - * trailing newline from Curl_get_line we can treat length 1 as empty. - */ - if((*lineptr == '#') || strlen(lineptr) <= 1) - continue; + /* Skip empty or commented lines, since we know the line will have + a trailing newline from Curl_get_line we can treat length 1 as + empty. */ + if((*lineptr == '#') || strlen(lineptr) <= 1) + continue; - hsts_add(h, lineptr); - } - } while(!result && !eof); - curlx_dyn_free(&buf); /* free the line buffer */ + hsts_add(h, lineptr); + } + } while(!result && !eof); + curlx_dyn_free(&buf); /* free the line buffer */ + } curlx_fclose(fp); } return result;
diff --git a/Utilities/cmcurl/lib/hsts.h b/Utilities/cmcurl/lib/hsts.h index e0f6363..0e6585f 100644 --- a/Utilities/cmcurl/lib/hsts.h +++ b/Utilities/cmcurl/lib/hsts.h
@@ -28,6 +28,8 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS) #include "llist.h" +#define MAX_HSTS_ENTRIES 10000 + #if defined(DEBUGBUILD) || defined(UNITTESTS) extern time_t deltatime; #endif
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 188da5f..9118c7e 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c
@@ -166,17 +166,29 @@ } #endif +/* If the header has a value, this function returns TRUE and the value is in + 'outp' with blanks trimmed off. +*/ +static bool header_has_value(const char **headerp, struct Curl_str *outp) +{ + bool value = !curlx_str_cspn(headerp, outp, ";:") && + (!curlx_str_single(headerp, ':') || !curlx_str_single(headerp, ';')); + + if(value) { + curlx_str_untilnl(headerp, outp, MAX_HTTP_RESP_HEADER_SIZE); + curlx_str_trimblanks(outp); + } + return value; +} + static bool http_header_is_empty(const char *header) { struct Curl_str out; - if(!curlx_str_cspn(&header, &out, ";:") && - (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) { - curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE); - curlx_str_trimblanks(&out); + if(header_has_value(&header, &out)) { return curlx_strlen(&out) == 0; } - return TRUE; /* invalid head format, treat as empty */ + return TRUE; /* invalid header format, treat as empty */ } /* @@ -192,11 +204,7 @@ struct Curl_str out; /* find the end of the header name */ - if(!curlx_str_cspn(&header, &out, ";:") && - (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) { - curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE); - curlx_str_trimblanks(&out); - + if(header_has_value(&header, &out)) { *valp = curlx_memdup0(curlx_str(&out), curlx_strlen(&out)); if(*valp) return CURLE_OK; @@ -214,6 +222,8 @@ * * This function MUST be used after the header has already been confirmed to * lead with "word:". + * + * @unittest: 1626 */ char *Curl_copy_header_value(const char *header) { @@ -254,7 +264,7 @@ connection */ if(proxy) { #ifndef CURL_DISABLE_PROXY - userp = &data->state.aptr.proxyuserpwd; + userp = &data->req.proxyuserpwd; user = data->state.aptr.proxyuser; pwd = data->state.aptr.proxypasswd; #else @@ -262,7 +272,7 @@ #endif } else { - userp = &data->state.aptr.userpwd; + userp = &data->req.userpwd; user = data->state.aptr.user; pwd = data->state.aptr.passwd; } @@ -310,7 +320,7 @@ char **userp; CURLcode result = CURLE_OK; - userp = &data->state.aptr.userpwd; + userp = &data->req.userpwd; curlx_free(*userp); *userp = curl_maprintf("Authorization: Bearer %s\r\n", data->set.str[STRING_BEARER]); @@ -470,53 +480,53 @@ DEBUGASSERT(data->conn); /* - ** If we have not been asked to fail on error, - ** do not fail. - */ + * If we have not been asked to fail on error, + * do not fail. + */ if(!data->set.http_fail_on_error) return FALSE; /* - ** Any code < 400 is never terminal. - */ + * Any code < 400 is never terminal. + */ if(httpcode < 400) return FALSE; /* - ** A 416 response to a resume request is presumably because the file is - ** already completely downloaded and thus not actually a fail. - */ + * A 416 response to a resume request is presumably because the file is + * already completely downloaded and thus not actually a fail. + */ if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET && httpcode == 416) return FALSE; /* - ** Any code >= 400 that is not 401 or 407 is always - ** a terminal error - */ + * Any code >= 400 that is not 401 or 407 is always + * a terminal error + */ if((httpcode != 401) && (httpcode != 407)) return TRUE; /* - ** All we have left to deal with is 401 and 407 - */ + * All we have left to deal with is 401 and 407 + */ DEBUGASSERT((httpcode == 401) || (httpcode == 407)); /* - ** Examine the current authentication state to see if this is an error. The - ** idea is for this function to get called after processing all the headers - ** in a response message. So, if we have been to asked to authenticate a - ** particular stage, and we have done it, we are OK. If we are already - ** completely authenticated, it is not OK to get another 401 or 407. - ** - ** It is possible for authentication to go stale such that the client needs - ** to reauthenticate. Once that info is available, use it here. - */ + * Examine the current authentication state to see if this is an error. The + * idea is for this function to get called after processing all the headers + * in a response message. If we have been to asked to authenticate + * a particular stage, and we have done it, we are OK. If we are already + * completely authenticated, it is not OK to get another 401 or 407. + * + * It is possible for authentication to go stale such that the client needs + * to reauthenticate. Once that info is available, use it here. + */ /* - ** Either we are not authenticating, or we are supposed to be authenticating - ** something else. This is an error. - */ + * Either we are not authenticating, or we are supposed to be authenticating + * something else. This is an error. + */ if((httpcode == 401) && !data->state.aptr.user) return TRUE; #ifndef CURL_DISABLE_PROXY @@ -721,6 +731,10 @@ if(auth) { #ifndef CURL_DISABLE_PROXY + if(proxy) + data->info.proxyauthpicked = authstatus->picked; + else + data->info.httpauthpicked = authstatus->picked; infof(data, "%s auth using %s with user '%s'", proxy ? "Proxy" : "Server", auth, proxy ? (data->state.aptr.proxyuser ? @@ -735,8 +749,13 @@ #endif authstatus->multipass = !authstatus->done; } - else + else { authstatus->multipass = FALSE; + if(proxy) + data->info.proxyauthpicked = 0; + else + data->info.httpauthpicked = 0; + } return result; } @@ -755,14 +774,14 @@ * * @returns CURLcode */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel) /* TRUE if this is the request setting - up the proxy tunnel */ +CURLcode Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel) /* TRUE if this is + the request setting up + the proxy tunnel */ { CURLcode result = CURLE_OK; struct auth *authhost; @@ -943,8 +962,10 @@ struct auth *authp, uint32_t *availp) { - if(authp->avail & CURLAUTH_DIGEST) + if(authp->avail & CURLAUTH_DIGEST) { + *availp |= CURLAUTH_DIGEST; infof(data, "Ignoring duplicate digest auth header."); + } else if(Curl_auth_is_digest_supported()) { CURLcode result; @@ -1113,6 +1134,11 @@ Curl_creader_set_rewind(data, FALSE); } +#define HTTPREQ_IS_POST(data) \ + ((data)->state.httpreq == HTTPREQ_POST || \ + (data)->state.httpreq == HTTPREQ_POST_FORM || \ + (data)->state.httpreq == HTTPREQ_POST_MIME) + CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl, followtype type) { @@ -1201,60 +1227,43 @@ return CURLE_OUT_OF_MEMORY; } else { - uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0); - if(uc) + bool same_origin; + CURLcode result; + CURLU *u = curl_url(); + if(!u) + return CURLE_OUT_OF_MEMORY; + uc = curl_url_set(u, CURLUPART_URL, + Curl_bufref_ptr(&data->state.url), + CURLU_URLENCODE | CURLU_ALLOW_SPACE); + if(!uc) + uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0); + if(uc) { + curl_url_cleanup(u); 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)) { - int port; - bool clear = FALSE; + same_origin = Curl_url_same_origin(u, data->state.uh); + curl_url_cleanup(u); - if(data->set.use_port && data->state.allow_port) - /* a custom port is used */ - port = (int)data->set.use_port; - else { - curl_off_t value; - char *portnum; - const char *p; - uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum, - CURLU_DEFAULT_PORT); - if(uc) { - curlx_free(follow_url); - return Curl_uc_to_curlcode(uc); - } - p = portnum; - curlx_str_number(&p, &value, 0xffff); - port = (int)value; - curlx_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_scheme *p; - uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0); - if(uc) { - curlx_free(follow_url); - return Curl_uc_to_curlcode(uc); - } +#ifndef CURL_DISABLE_DIGEST_AUTH + if(!same_origin) + Curl_auth_digest_cleanup(&data->state.digest); +#endif - p = Curl_get_scheme(scheme); - if(p && (p->protocol != data->info.conn_protocol)) { - infof(data, "Clear auth, redirects scheme from %s to %s", - data->info.conn_scheme, scheme); - clear = TRUE; - } - curlx_free(scheme); + if((!same_origin && !data->set.allow_auth_to_other_hosts) || + !data->set.str[STRING_USERNAME]) { + result = Curl_reset_userpwd(data); + if(result) { + curlx_free(follow_url); + return result; } - if(clear) { - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); - } + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); + } + result = Curl_reset_proxypwd(data); + if(result) { + curlx_free(follow_url); + return result; } } DEBUGASSERT(follow_url); @@ -1321,10 +1330,7 @@ * 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.post301) { + if(HTTPREQ_IS_POST(data) && !data->set.post301) { http_switch_to_get(data, 301); switch_to_get = TRUE; } @@ -1346,10 +1352,7 @@ * 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.post302) { + if(HTTPREQ_IS_POST(data) && !data->set.post302) { http_switch_to_get(data, 302); switch_to_get = TRUE; } @@ -1359,13 +1362,8 @@ /* '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.post303)) { + if(!HTTPREQ_IS_POST(data) || !data->set.post303) { http_switch_to_get(data, 303); switch_to_get = TRUE; } @@ -1400,8 +1398,10 @@ /* * Curl_compareheader() * - * Returns TRUE if 'headerline' contains the 'header' with given 'content'. - * Pass headers WITH the colon. + * Returns TRUE if 'headerline' contains the 'header' with given 'content' + * (within a comma-separated list of tokens). Pass 'header' WITH the colon. + * + * @unittest: 1625 */ bool Curl_compareheader(const char *headerline, /* line to check */ const char *header, /* header keyword _with_ colon */ @@ -1435,9 +1435,24 @@ if(curlx_strlen(&val) >= clen) { size_t len; p = curlx_str(&val); - for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) { - if(curl_strnequal(p, content, clen)) + for(len = curlx_strlen(&val); len >= clen;) { + struct Curl_str next; + const char *o = p; + /* after a match there must be a comma, space, newline or null byte */ + if(curl_strnequal(p, content, clen) && + ((p[clen] == ',') || ISBLANK(p[clen]) || ISNEWLINE(p[clen]) || + !p[clen])) return TRUE; /* match! */ + /* advance to the next comma */ + if(curlx_str_until(&p, &next, MAX_HTTP_RESP_HEADER_SIZE, ',') || + curlx_str_single(&p, ',')) + break; /* no comma, get out */ + + /* if there are more dummy commas, move over them as well */ + do + curlx_str_passblanks(&p); + while(!curlx_str_single(&p, ',')); + len -= (p - o); } } return FALSE; /* no match */ @@ -1482,7 +1497,7 @@ /* We are now waiting for a reply from the server or * a timeout on our side IFF the request has been fully sent. */ DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, " - "timeout %dms", data->set.expect_100_timeout)); + "timeout %dms", data->set.expect_100_timeout)); ctx->state = EXP100_AWAITING_CONTINUE; ctx->start = *Curl_pgrs_now(data); Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT); @@ -1598,7 +1613,7 @@ struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - if(CURL_WANT_RECV(data)) { + if(CURL_REQ_WANT_RECV(data)) { result = Curl_pollset_add_in(data, ps, conn->sock[FIRSTSOCKET]); } @@ -1973,7 +1988,7 @@ static CURLcode http_useragent(struct Curl_easy *data) { - /* The User-Agent string might have been allocated in url.c already, because + /* The User-Agent string might have been allocated already, because it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ @@ -2001,7 +2016,10 @@ data->state.first_remote_port = conn->remote_port; data->state.first_remote_protocol = conn->scheme->protocol; } - Curl_safefree(aptr->host); + curlx_safefree(aptr->host); +#ifndef CURL_DISABLE_COOKIES + curlx_safefree(data->req.cookiehost); +#endif ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || @@ -2037,8 +2055,7 @@ if(colon) *colon = 0; /* The host must not include an embedded port number */ } - curlx_free(aptr->cookiehost); - aptr->cookiehost = cookiehost; + data->req.cookiehost = cookiehost; } #endif @@ -2049,9 +2066,9 @@ } } else { - /* When building Host: headers, we must put the hostname within - [brackets] if the hostname is a plain IPv6-address. RFC2732-style. */ - const char *host = conn->host.name; + /* Use the hostname as present in the URL if it was IPv6. */ + char *host = (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name; if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && (conn->remote_port == PORT_HTTPS)) || @@ -2059,14 +2076,9 @@ (conn->remote_port == PORT_HTTP))) /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include the port number in the host string */ - aptr->host = curl_maprintf("Host: %s%s%s\r\n", - conn->bits.ipv6_ip ? "[" : "", - host, conn->bits.ipv6_ip ? "]" : ""); + aptr->host = curl_maprintf("Host: %s\r\n", host); else - aptr->host = curl_maprintf("Host: %s%s%s:%d\r\n", - conn->bits.ipv6_ip ? "[" : "", - host, conn->bits.ipv6_ip ? "]" : "", - conn->remote_port); + aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->remote_port); if(!aptr->host) /* without Host: we cannot make a nice request */ @@ -2097,7 +2109,7 @@ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { /* Using a proxy but does not tunnel through it */ - /* The path sent to the proxy is in fact the entire URL. But if the remote + /* The path sent to the proxy is in fact the entire URL, but if the remote host is a IDN-name, we must make sure that the request we produce only uses the encoded hostname! */ @@ -2209,7 +2221,7 @@ result = Curl_getformdata(data, data->state.formp, data->set.httppost, data->state.fread_func); if(result) { - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); return result; } data->state.mimepost = data->state.formp; @@ -2538,8 +2550,8 @@ if(data->cookies && data->state.cookie_engine) { bool okay; - const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : data->conn->host.name; + const char *host = data->req.cookiehost ? + data->req.cookiehost : data->conn->host.name; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); result = Curl_cookie_getlist(data, data->conn, &okay, host, &list); if(!result && okay) { @@ -2663,7 +2675,7 @@ if(conn->bits.close) { /* Abort after the headers if "follow Location" is set and we are set to close anyway. */ - k->keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); k->done = TRUE; return CURLE_OK; } @@ -2682,7 +2694,7 @@ infof(data, "The entire document is already downloaded"); streamclose(conn, "already downloaded"); /* Abort download */ - k->keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); k->done = TRUE; return CURLE_OK; } @@ -2885,14 +2897,14 @@ #ifndef CURL_DISABLE_PROXY case H1_HD_PROXY_AUTH: - if(data->state.aptr.proxyuserpwd) - result = curlx_dyn_add(req, data->state.aptr.proxyuserpwd); + if(data->req.proxyuserpwd) + result = curlx_dyn_add(req, data->req.proxyuserpwd); break; #endif case H1_HD_USER_AUTH: - if(data->state.aptr.userpwd) - result = curlx_dyn_add(req, data->state.aptr.userpwd); + if(data->req.userpwd) + result = curlx_dyn_add(req, data->req.userpwd); break; case H1_HD_RANGE: @@ -2923,7 +2935,7 @@ break; case H1_HD_ACCEPT_ENCODING: - Curl_safefree(data->state.aptr.accept_encoding); + curlx_safefree(data->state.aptr.accept_encoding); if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && data->set.str[STRING_ENCODING]) result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n", @@ -2931,7 +2943,7 @@ break; case H1_HD_REFERER: - Curl_safefree(data->state.aptr.ref); + curlx_safefree(data->state.aptr.ref); if(Curl_bufref_ptr(&data->state.referer) && !Curl_checkheaders(data, STRCONST("Referer"))) result = curlx_dyn_addf(req, "Referer: %s\r\n", @@ -2955,7 +2967,7 @@ #ifndef CURL_DISABLE_ALTSVC case H1_HD_ALT_USED: if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) - result = curlx_dyn_addf(req, "Alt-Used: %s:%d\r\n", + result = curlx_dyn_addf(req, "Alt-Used: %s:%u\r\n", conn->conn_to_host.name, conn->conn_to_port); break; @@ -3103,12 +3115,6 @@ if(result == CURLE_TOO_LARGE) failf(data, "HTTP request too large"); - /* clear userpwd and proxyuserpwd to avoid reusing old credentials - * from reused connections */ - Curl_safefree(data->state.aptr.userpwd); -#ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuserpwd); -#endif curlx_dyn_free(&req); return result; } @@ -3156,13 +3162,13 @@ static statusline checkrtspprefix(struct Curl_easy *data, const char *s, size_t len) { - statusline result = STATUS_BAD; + statusline status = STATUS_BAD; statusline onmatch = len >= 5 ? STATUS_DONE : STATUS_UNKNOWN; (void)data; if(checkprefixmax("RTSP/", s, len)) - result = onmatch; + status = onmatch; - return result; + return status; } #endif /* CURL_DISABLE_RTSP */ @@ -3205,7 +3211,7 @@ const char *v; struct connectdata *conn = data->conn; v = (data->asi && - (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) || + (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_ALTSVC_HTTP") @@ -3545,8 +3551,8 @@ if(v) { /* If there is a custom-set Host: name, use it here, or else use * real peer hostname. */ - const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : conn->host.name; + const char *host = data->req.cookiehost ? + data->req.cookiehost : conn->host.name; const bool secure_context = Curl_secure_context(conn, host); CURLcode result; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); @@ -3559,7 +3565,7 @@ #ifndef CURL_DISABLE_HSTS /* If enabled, the header is incoming and this is over HTTPS */ v = (data->hsts && - (Curl_conn_is_ssl(conn, FIRSTSOCKET) || + (Curl_xfer_is_secure(data) || #ifdef DEBUGBUILD /* allow debug builds to circumvent the HTTPS restriction */ getenv("CURL_HSTS_HTTP") @@ -3569,11 +3575,11 @@ ) ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; if(v) { - CURLcode check = + CURLcode result = Curl_hsts_parse(data->hsts, conn->host.name, v); - if(check) { - if(check == CURLE_OUT_OF_MEMORY) - return check; + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + return result; infof(data, "Illegal STS header skipped"); } #ifdef DEBUGBUILD @@ -3726,13 +3732,13 @@ /* no major version switch mid-connection */ if(k->httpversion_sent && (k->httpversion / 10 != k->httpversion_sent / 10)) { - failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)", + failf(data, "Version mismatch (from HTTP/%d to HTTP/%d)", k->httpversion_sent / 10, k->httpversion / 10); return CURLE_WEIRD_SERVER_REPLY; } break; default: - failf(data, "Unsupported HTTP version (%u.%d) in response", + failf(data, "Unsupported HTTP version (%d.%d) in response", k->httpversion / 10, k->httpversion % 10); return CURLE_UNSUPPORTED_PROTOCOL; } @@ -3875,136 +3881,112 @@ return CURLE_OK; } -static CURLcode http_on_response(struct Curl_easy *data, - const char *last_hd, size_t last_hd_len, - const char *buf, size_t blen, - size_t *pconsumed) +/* + * Handle a 101 Switching Protocols response. Performs the actual protocol + * upgrade to HTTP/2 or WebSocket based on what was requested. + */ +static CURLcode http_on_101_upgrade(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed, + bool *conn_changed) { struct connectdata *conn = data->conn; - CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; - bool conn_changed = FALSE; - (void)buf; /* not used without HTTP2 enabled */ - *pconsumed = 0; - - if(k->upgr101 == UPGR101_RECEIVED) { - /* supposedly upgraded to http2 now */ - if(data->req.httpversion != 20) - infof(data, "Lying server, not serving HTTP/2"); - } - - if(k->httpcode < 200 && last_hd) { - /* Intermediate responses might trigger processing of more - * responses, write the last header to the client before - * proceeding. */ - result = http_write_header(data, last_hd, last_hd_len); - last_hd = NULL; /* handled it */ - if(result) - goto out; - } - - if(k->httpcode < 100) { - failf(data, "Unsupported response code in HTTP response"); - result = CURLE_UNSUPPORTED_PROTOCOL; - goto out; - } - else if(k->httpcode < 200) { - /* "A user agent MAY ignore unexpected 1xx status responses." - * By default, we expect to get more responses after this one. */ - k->header = TRUE; - k->headerline = 0; /* restart the header line counter */ - - switch(k->httpcode) { - case 100: - /* - * We have made an HTTP PUT or POST and this is 1.1-lingo - * that tells us that the server is OK with this and ready - * to receive the data. - */ - http_exp100_got100(data); - break; - case 101: { - int upgr101_requested = k->upgr101; - - if(k->httpversion_sent != 11) { - /* invalid for other HTTP versions */ - failf(data, "server sent 101 response while not talking HTTP/1.1"); - result = CURLE_WEIRD_SERVER_REPLY; - goto out; - } - - /* Whatever the success, upgrade was selected. */ - k->upgr101 = UPGR101_RECEIVED; - data->conn->bits.upgrade_in_progress = FALSE; - conn_changed = TRUE; - - /* To be fully conform, we would check the "Upgrade:" response header - * to mention the protocol we requested. */ - switch(upgr101_requested) { - case UPGR101_H2: - /* Switch to HTTP/2, where we will get more responses. - * blen bytes in bug are already h2 protocol bytes */ - infof(data, "Received 101, Switching to HTTP/2"); - result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); - if(result) - goto out; - *pconsumed += blen; - break; -#ifndef CURL_DISABLE_WEBSOCKETS - case UPGR101_WS: - /* Switch to WebSocket, where we now stream ws frames. - * blen bytes in bug are already ws protocol bytes */ - infof(data, "Received 101, Switching to WebSocket"); - result = Curl_ws_accept(data, buf, blen); - if(result) - goto out; - *pconsumed += blen; /* ws accept handled the data */ - break; +#if !defined(USE_NGHTTP2) && defined(CURL_DISABLE_WEBSOCKETS) + (void)buf; + (void)blen; + (void)pconsumed; +#else + CURLcode result; + int upgr101_requested = k->upgr101; #endif - default: - /* We silently accept this as the final response. What are we - * switching to if we did not ask for an Upgrade? Maybe the - * application provided an `Upgrade: xxx` header? */ - k->header = FALSE; - break; - } - /* processed 101 */ - break; - } - default: - /* The server may send us other 1xx responses, like informative - * 103. This have no influence on request processing and we expect - * to receive a final response eventually. */ - break; - } - goto out; + + if(k->httpversion_sent != 11) { + /* invalid for other HTTP versions */ + failf(data, "server sent 101 response while not talking HTTP/1.1"); + return CURLE_WEIRD_SERVER_REPLY; } - /* k->httpcode >= 200, final response */ + /* Whatever the success, upgrade was selected. */ + k->upgr101 = UPGR101_RECEIVED; + conn->bits.upgrade_in_progress = FALSE; + *conn_changed = TRUE; + + /* To be fully compliant, we would check the "Upgrade:" response header to + * mention the protocol we requested. */ +#ifdef USE_NGHTTP2 + if(upgr101_requested == UPGR101_H2) { + /* Switch to HTTP/2, where we will get more responses. blen bytes in buf + * are already h2 protocol bytes */ + infof(data, "Received 101, Switching to HTTP/2"); + result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen); + if(!result) + *pconsumed += blen; + return result; + } +#endif +#ifndef CURL_DISABLE_WEBSOCKETS + if(upgr101_requested == UPGR101_WS) { + /* Switch to WebSocket, where we now stream ws frames. blen bytes in buf + * are already ws protocol bytes */ + infof(data, "Received 101, Switching to WebSocket"); + result = Curl_ws_accept(data, buf, blen); + if(!result) + *pconsumed += blen; /* ws accept handled the data */ + return result; + } +#endif + /* We silently accept this as the final response. What are we switching to + * if we did not ask for an Upgrade? Maybe the application provided an + * `Upgrade: xxx` header? */ k->header = FALSE; - if(data->conn->bits.upgrade_in_progress) { - /* Asked for protocol upgrade, but it was not selected by the server */ - data->conn->bits.upgrade_in_progress = FALSE; - conn_changed = TRUE; - } + return CURLE_OK; +} - if((k->size == -1) && !k->chunk && !conn->bits.close && - (k->httpversion == 11) && - !(conn->scheme->protocol & CURLPROTO_RTSP) && - data->state.httpreq != HTTPREQ_HEAD) { - /* On HTTP 1.1, when connection is not to get closed, but no - Content-Length nor Transfer-Encoding chunked have been - received, according to RFC2616 section 4.4 point 5, we - assume that the server will close the connection to - signal the end of the document. */ - infof(data, "no chunk, no close, no size. Assume close to " - "signal end"); - streamclose(conn, "HTTP: No end-of-message indicator"); - } +/* + * Handle 1xx intermediate HTTP responses. Sets up state for more + * headers and processes 100-continue and 101 upgrade responses. + */ +static CURLcode http_on_1xx_response(struct Curl_easy *data, + const char *buf, size_t blen, + size_t *pconsumed, + bool *conn_changed) +{ + struct SingleRequest *k = &data->req; - /* At this point we have some idea about the fate of the connection. - If we are closing the connection it may result auth failure. */ + /* "A user agent MAY ignore unexpected 1xx status responses." + * By default, we expect to get more responses after this one. */ + k->header = TRUE; + k->headerline = 0; /* restart the header line counter */ + + switch(k->httpcode) { + case 100: + /* We have made an HTTP PUT or POST and this is 1.1-lingo that tells us + * that the server is OK with this and ready to receive the data. */ + http_exp100_got100(data); + break; + case 101: + return http_on_101_upgrade(data, buf, blen, pconsumed, conn_changed); + default: + /* The server may send us other 1xx responses, like informative 103. This + * has no influence on request processing and we expect to receive a + * final response eventually. */ + break; + } + return CURLE_OK; +} + +#if defined(USE_NTLM) || defined(USE_SPNEGO) +/* + * Check if NTLM or SPNEGO authentication negotiation failed due to + * connection closure (typically on HTTP/1.0 servers). + */ +static void http_check_auth_closure(struct Curl_easy *data, + struct connectdata *conn) +{ + /* At this point we have some idea about the fate of the connection. If we + are closing the connection it may result auth failure. */ #ifdef USE_NTLM if(conn->bits.close && (((data->req.httpcode == 401) && @@ -4033,9 +4015,154 @@ conn->proxy_negotiate_state = GSS_AUTHSUCC; } #endif +} +#else +#define http_check_auth_closure(x, y) /* empty */ +#endif + +/* + * Handle an error response (>= 300) received while still sending the + * request body. Deals with 417 Expectation Failed retries, keep-sending + * on error, and aborting the send. + */ +static CURLcode http_handle_send_error(struct Curl_easy *data) +{ + struct connectdata *conn = data->conn; + struct SingleRequest *k = &data->req; + CURLcode result = CURLE_OK; + + if(!data->req.authneg && !conn->bits.close && + !Curl_creader_will_rewind(data)) { + /* + * General treatment of errors when about to send data. + * Including: "417 Expectation Failed", while waiting for + * 100-continue. + * + * The check for close above is done because if something + * else has already deemed the connection to get closed then + * something else should have considered the big picture and + * we avoid this check. + */ + + switch(data->state.httpreq) { + case HTTPREQ_PUT: + case HTTPREQ_POST: + case HTTPREQ_POST_FORM: + case HTTPREQ_POST_MIME: + /* We got an error response. If this happened before the + * whole request body has been sent we stop sending and + * mark the connection for closure after we have read the + * entire response. */ + if(!Curl_req_done_sending(data)) { + if((k->httpcode == 417) && http_exp100_is_selected(data)) { + /* 417 Expectation Failed - try again without the + Expect header */ + if(!k->writebytecount && http_exp100_is_waiting(data)) { + infof(data, "Got HTTP failure 417 while waiting for a 100"); + } + else { + infof(data, "Got HTTP failure 417 while sending data"); + streamclose(conn, "Stop sending data before everything sent"); + result = http_perhapsrewind(data, conn); + if(result) + return result; + } + data->state.disableexpect = TRUE; + Curl_req_abort_sending(data); + DEBUGASSERT(!data->req.newurl); + data->req.newurl = Curl_bufref_dup(&data->state.url); + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + } + else if(data->set.http_keep_sending_on_error) { + infof(data, "HTTP error before end of send, keep sending"); + http_exp100_send_anyway(data); + } + else { + infof(data, "HTTP error before end of send, stop sending"); + streamclose(conn, "Stop sending data before everything sent"); + result = Curl_req_abort_sending(data); + if(result) + return result; + } + } + break; + + default: /* default label present to avoid compiler warnings */ + break; + } + } + + if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) { + /* We rewind before next send, continue sending now */ + infof(data, "Keep sending data to get tossed away"); + CURL_REQ_SET_SEND(data); + } + return result; +} + +static CURLcode http_on_response(struct Curl_easy *data, + const char *last_hd, size_t last_hd_len, + const char *buf, size_t blen, + size_t *pconsumed) +{ + struct connectdata *conn = data->conn; + CURLcode result = CURLE_OK; + struct SingleRequest *k = &data->req; + bool conn_changed = FALSE; + + (void)buf; /* not used without HTTP2 enabled */ + *pconsumed = 0; + + if(k->upgr101 == UPGR101_RECEIVED) { + /* supposedly upgraded to http2 now */ + if(data->req.httpversion != 20) + infof(data, "Lying server, not serving HTTP/2"); + } + + if(k->httpcode < 200 && last_hd) { + /* Intermediate responses might trigger processing of more responses, + * write the last header to the client before proceeding. */ + result = http_write_header(data, last_hd, last_hd_len); + last_hd = NULL; /* handled it */ + if(result) + goto out; + } + + if(k->httpcode < 100) { + failf(data, "Unsupported response code in HTTP response"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; + } + else if(k->httpcode < 200) { + result = http_on_1xx_response(data, buf, blen, pconsumed, &conn_changed); + goto out; + } + + /* k->httpcode >= 200, final response */ + k->header = FALSE; + if(conn->bits.upgrade_in_progress) { + /* Asked for protocol upgrade, but it was not selected */ + conn->bits.upgrade_in_progress = FALSE; + conn_changed = TRUE; + } + + if((k->size == -1) && !k->chunk && !conn->bits.close && + (k->httpversion == 11) && + !(conn->scheme->protocol & CURLPROTO_RTSP) && + data->state.httpreq != HTTPREQ_HEAD) { + /* On HTTP 1.1, when connection is not to get closed, but no + Content-Length nor Transfer-Encoding chunked have been received, + according to RFC2616 section 4.4 point 5, we assume that the server + will close the connection to signal the end of the document. */ + infof(data, "no chunk, no close, no size. Assume close to signal end"); + streamclose(conn, "HTTP: No end-of-message indicator"); + } + + http_check_auth_closure(data, conn); #ifndef CURL_DISABLE_WEBSOCKETS - /* All >=200 HTTP status codes are errors when wanting WebSocket */ + /* All >=200 HTTP status codes are errors when wanting ws */ if(data->req.upgr101 == UPGR101_WS) { failf(data, "Refused WebSocket upgrade: %d", k->httpcode); result = CURLE_HTTP_RETURNED_ERROR; @@ -4045,88 +4172,23 @@ /* Check if this response means the transfer errored. */ if(http_should_fail(data, data->req.httpcode)) { - failf(data, "The requested URL returned error: %d", k->httpcode); + failf(data, "The requested URL returned error: %d", + k->httpcode); result = CURLE_HTTP_RETURNED_ERROR; goto out; } - /* Curl_http_auth_act() checks what authentication methods - * that are available and decides which one (if any) to - * use. It will set 'newurl' if an auth method was picked. */ + /* Curl_http_auth_act() checks what authentication methods that are + * available and decides which one (if any) to use. It will set 'newurl' if + * an auth method was picked. */ result = Curl_http_auth_act(data); if(result) goto out; if(k->httpcode >= 300) { - if(!data->req.authneg && !conn->bits.close && - !Curl_creader_will_rewind(data)) { - /* - * General treatment of errors when about to send data. Including : - * "417 Expectation Failed", while waiting for 100-continue. - * - * The check for close above is done because of something - * else has already deemed the connection to get closed then - * something else should have considered the big picture and we - * avoid this check. - * - */ - - switch(data->state.httpreq) { - case HTTPREQ_PUT: - case HTTPREQ_POST: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - /* We got an error response. If this happened before the whole - * request body has been sent we stop sending and mark the - * connection for closure after we have read the entire response. - */ - if(!Curl_req_done_sending(data)) { - if((k->httpcode == 417) && http_exp100_is_selected(data)) { - /* 417 Expectation Failed - try again without the Expect - header */ - if(!k->writebytecount && http_exp100_is_waiting(data)) { - infof(data, "Got HTTP failure 417 while waiting for a 100"); - } - else { - infof(data, "Got HTTP failure 417 while sending data"); - streamclose(conn, "Stop sending data before everything sent"); - result = http_perhapsrewind(data, conn); - if(result) - goto out; - } - data->state.disableexpect = TRUE; - Curl_req_abort_sending(data); - DEBUGASSERT(!data->req.newurl); - data->req.newurl = Curl_bufref_dup(&data->state.url); - if(!data->req.newurl) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - } - else if(data->set.http_keep_sending_on_error) { - infof(data, "HTTP error before end of send, keep sending"); - http_exp100_send_anyway(data); - } - else { - infof(data, "HTTP error before end of send, stop sending"); - streamclose(conn, "Stop sending data before everything sent"); - result = Curl_req_abort_sending(data); - if(result) - goto out; - } - } - break; - - default: /* default label present to avoid compiler warnings */ - break; - } - } - - if(Curl_creader_will_rewind(data) && !Curl_req_done_sending(data)) { - /* We rewind before next send, continue sending now */ - infof(data, "Keep sending data to get tossed away"); - k->keepon |= KEEP_SEND; - } + result = http_handle_send_error(data); + if(result) + goto out; } /* If we requested a "no body", this is a good time to get @@ -4135,11 +4197,10 @@ if(data->req.no_body) k->download_done = TRUE; - /* If max download size is *zero* (nothing) we already have - nothing and can safely return ok now! But for HTTP/2, we would - like to call http2_handle_stream_close to properly close a - stream. In order to do this, we keep reading until we - close the stream. */ + /* If max download size is *zero* (nothing) we already have nothing and can + safely return ok now! For HTTP/2, we would like to call + http2_handle_stream_close to properly close a stream. In order to do + this, we keep reading until we close the stream. */ if((k->maxdownload == 0) && (k->httpversion_sent < 20)) k->download_done = TRUE; @@ -4147,21 +4208,18 @@ result = http_firstwrite(data); if(!result) - /* This is the last response that we get for the current request. - * Check on the body size and determine if the response is complete. - */ + /* This is the last response that we get for the current request. Check on + * the body size and determine if the response is complete. */ result = http_size(data); out: - if(last_hd) { + if(last_hd) /* if not written yet, write it now */ - result = Curl_1st_err( - result, http_write_header(data, last_hd, last_hd_len)); - } - if(conn_changed) { - /* poke the multi handle to allow any pending pipewait to retry now */ + result = Curl_1st_fatal(result, + http_write_header(data, last_hd, last_hd_len)); + if(conn_changed) + /* poke the multi handle to allow pending pipewait to retry */ Curl_multi_connchanged(data->multi); - } return result; } @@ -4173,7 +4231,7 @@ CURLcode result = CURLE_OK; struct SingleRequest *k = &data->req; int writetype; - DEBUGASSERT(!hd[hdlen]); /* null terminated */ + DEBUGASSERT(!hd[hdlen]); /* null-terminated */ *pconsumed = 0; if((0x0a == *hd) || (0x0d == *hd)) { @@ -4534,18 +4592,18 @@ CURLcode result; size_t consumed; char tmp = 0; - DEBUGASSERT(!hd[hdlen]); /* null terminated */ + DEBUGASSERT(!hd[hdlen]); /* null-terminated */ result = http_rw_hd(data, hd, hdlen, &tmp, 0, &consumed); if(!result && is_eos) { - result = Curl_client_write(data, (CLIENTWRITE_BODY|CLIENTWRITE_EOS), + result = Curl_client_write(data, (CLIENTWRITE_BODY | CLIENTWRITE_EOS), &tmp, 0); } return result; } /* - * HTTP protocol `write_resp` implementation. Will parse headers + * HTTP protocol `write_resp` implementation. Parse headers * when not done yet and otherwise return without consuming data. */ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data, @@ -4892,7 +4950,7 @@ infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme); } else { - scheme = Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ? "https" : "http"; + scheme = Curl_xfer_is_secure(data) ? "https" : "http"; } } @@ -4982,7 +5040,7 @@ /* * HTTP handler interface. */ -static const struct Curl_protocol Curl_protocol_http = { +const struct Curl_protocol Curl_protocol_http = { Curl_http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ @@ -4997,43 +5055,9 @@ ZERO_NULL, /* disconnect */ Curl_http_write_resp, /* write_resp */ Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ Curl_http_follow, /* follow */ }; #endif /* CURL_DISABLE_HTTP */ - -/* - * HTTP handler interface. - */ -const struct Curl_scheme Curl_scheme_http = { - "http", /* scheme */ -#ifdef CURL_DISABLE_HTTP - ZERO_NULL, -#else - &Curl_protocol_http, -#endif - CURLPROTO_HTTP, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, - PORT_HTTP, /* defport */ -}; - -/* - * HTTPS handler interface. - */ -const struct Curl_scheme Curl_scheme_https = { - "https", /* scheme */ -#if defined(CURL_DISABLE_HTTP) || !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_http, -#endif - CURLPROTO_HTTPS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ - PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, - PORT_HTTPS, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h index bb53595..6e33c00 100644 --- a/Utilities/cmcurl/lib/http.h +++ b/Utilities/cmcurl/lib/http.h
@@ -37,27 +37,16 @@ HTTPREQ_HEAD } Curl_HttpReq; -/* When redirecting transfers. */ -typedef enum { - FOLLOW_NONE, /* not used within the function, a placeholder to allow - initing to this */ - FOLLOW_FAKE, /* only records stuff, not actually following */ - FOLLOW_RETRY, /* set if this is a request retry as opposed to a real - redirect following */ - FOLLOW_REDIR /* a full true redirect */ -} followtype; - #define CURL_HTTP_V1x (1 << 0) #define CURL_HTTP_V2x (1 << 1) #define CURL_HTTP_V3x (1 << 2) /* bitmask of CURL_HTTP_V* values */ typedef unsigned char http_majors; -extern const struct Curl_scheme Curl_scheme_http; -extern const struct Curl_scheme Curl_scheme_https; - #ifndef CURL_DISABLE_HTTP +extern const struct Curl_protocol Curl_protocol_http; + struct dynhds; struct http_negotiation { @@ -196,14 +185,14 @@ * * @returns CURLcode */ -CURLcode -Curl_http_output_auth(struct Curl_easy *data, - struct connectdata *conn, - const char *request, - Curl_HttpReq httpreq, - const char *path, - bool proxytunnel); /* TRUE if this is the request setting - up the proxy tunnel */ +CURLcode Curl_http_output_auth(struct Curl_easy *data, + struct connectdata *conn, + const char *request, + Curl_HttpReq httpreq, + const char *path, + bool proxytunnel); /* TRUE if this is + the request setting up + the proxy tunnel */ /* Decode HTTP status code string. */ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len);
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c index e1c5798..7be5abd 100644 --- a/Utilities/cmcurl/lib/http2.c +++ b/Utilities/cmcurl/lib/http2.c
@@ -154,7 +154,7 @@ size_t i; for(i = 0; i < stream->push_headers_used; i++) curlx_free(stream->push_headers[i]); - Curl_safefree(stream->push_headers); + curlx_safefree(stream->push_headers); stream->push_headers_used = 0; } @@ -203,6 +203,7 @@ { if(ctx->h2) { nghttp2_session_del(ctx->h2); + ctx->h2 = NULL; } } @@ -375,6 +376,10 @@ (void)cf; DEBUGASSERT(data); + + if(!data) + return CURLE_BAD_FUNCTION_ARGUMENT; + stream = H2_STREAM_CTX(ctx, data); if(stream) { *pstream = stream; @@ -713,8 +718,9 @@ return second; } -static int set_transfer_url(struct Curl_easy *data, - struct curl_pushheaders *hp) +static int set_transfer_url(struct Curl_easy *newhandle, + struct curl_pushheaders *hp, + struct Curl_easy *data) { const char *v; CURLUcode uc; @@ -752,6 +758,13 @@ } } + /* We can only allow PUSH of resource from the same origin, e.g. + * scheme + hostname + port */ + if(!Curl_url_same_origin(data->state.uh, u)) { + rc = 1; + goto fail; + } + uc = curl_url_get(u, CURLUPART_URL, &url, 0); if(uc) rc = 4; @@ -760,7 +773,7 @@ if(rc) return rc; - Curl_bufref_set(&data->state.url, url, 0, curl_free); + Curl_bufref_set(&newhandle->state.url, url, 0, curl_free); return 0; } @@ -806,7 +819,7 @@ heads.stream = stream; heads.frame = frame; - rv = set_transfer_url(newhandle, &heads); + rv = set_transfer_url(newhandle, &heads, data); if(rv) { CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d", frame->promised_stream_id, rv); @@ -863,7 +876,7 @@ newstream->id, newhandle); if(rv) { - infof(data, "failed to set user_data for stream %u", + infof(data, "failed to set user_data for stream %d", newstream->id); DEBUGASSERT(0); discard_newhandle(cf, newhandle); @@ -1010,7 +1023,7 @@ Curl_multi_mark_dirty(data); break; case NGHTTP2_WINDOW_UPDATE: - if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) { + if(CURL_REQ_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) { /* need more data, force processing of transfer */ Curl_multi_mark_dirty(data); } @@ -1099,7 +1112,7 @@ memcpy(scratch, frame->goaway.opaque_data, len); scratch[len] = '\0'; return curl_msnprintf(buffer, blen, - "FRAME[GOAWAY, error=%d, reason='%s', " + "FRAME[GOAWAY, error=%u, reason='%s', " "last_stream=%d]", frame->goaway.error_code, scratch, frame->goaway.last_stream_id); } @@ -1134,7 +1147,7 @@ if((frame->hd.type == NGHTTP2_GOAWAY) && !ctx->sent_goaway) { /* A GOAWAY not initiated by us, but by nghttp2 itself on detecting * a protocol error on the connection */ - failf(data, "nghttp2 shuts down connection with error %d: %s", + failf(data, "nghttp2 shuts down connection with error %u: %s", frame->goaway.error_code, nghttp2_http2_strerror(frame->goaway.error_code)); } @@ -1172,7 +1185,7 @@ session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS); ctx->enable_push = nghttp2_session_get_remote_settings( session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0; - CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d", + CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %u", ctx->max_concurrent_streams); CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s", ctx->enable_push ? "TRUE" : "false"); @@ -1187,7 +1200,7 @@ * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if(CURL_WANT_SEND(data)) + if(CURL_REQ_WANT_SEND(data)) Curl_multi_mark_dirty(data); } break; @@ -1197,7 +1210,7 @@ ctx->goaway_error = frame->goaway.error_code; ctx->remote_max_sid = frame->goaway.last_stream_id; if(data) { - infof(data, "received GOAWAY, error=%u, last_stream=%u", + infof(data, "received GOAWAY, error=%u, last_stream=%d", ctx->goaway_error, ctx->remote_max_sid); Curl_multi_connchanged(data->multi); } @@ -1327,7 +1340,7 @@ stream->reset = TRUE; if(stream->error) - CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)", + CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %u)", stream_id, nghttp2_http2_strerror(error_code), error_code); else CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id); @@ -1336,7 +1349,7 @@ /* remove `data_s` from the nghttp2 stream */ rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); if(rv) { - infof(data_s, "http/2: failed to clear user_data for stream %u", + infof(data_s, "http/2: failed to clear user_data for stream %d", stream_id); DEBUGASSERT(0); } @@ -1474,8 +1487,11 @@ stream->push_headers = headp; } h = curl_maprintf("%s:%s", name, value); - if(h) - stream->push_headers[stream->push_headers_used++] = h; + if(!h) { + free_push_headers(stream); + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + stream->push_headers[stream->push_headers_used++] = h; return 0; } @@ -1505,7 +1521,7 @@ cf_h2_header_error(cf, data_s, stream, result); return NGHTTP2_ERR_CALLBACK_FAILURE; } - hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r", + hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r", stream->status_code); result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO); if(result) { @@ -1692,9 +1708,9 @@ stream->close_handled = TRUE; return CURLE_OK; } - failf(data, "HTTP/2 stream %" PRIu32 " reset by %s (error 0x%" PRIx32 - " %s)", stream->id, stream->reset_by_server ? "server" : "curl", - stream->error, nghttp2_http2_strerror(stream->error)); + failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)", + stream->id, stream->reset_by_server ? "server" : "curl", + stream->error, nghttp2_http2_strerror(stream->error)); return stream->error ? CURLE_HTTP2_STREAM : (data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2); } @@ -1717,8 +1733,8 @@ break; curlx_dyn_reset(&dbuf); result = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a", - (int)e->namelen, e->name, - (int)e->valuelen, e->value); + (int)e->namelen, e->name, + (int)e->valuelen, e->value); if(result) break; Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf), @@ -1817,7 +1833,7 @@ /* Defer flushing during the connect phase so that the SETTINGS and * other initial frames are sent together with the first request. * Unless we are 'connect_only' where the request will never come. */ - if(!cf->connected && !cf->conn->connect_only) + if(!cf->connected && !cf->conn->bits.connect_only) return CURLE_OK; return nw_out_flush(cf, data); } @@ -1940,10 +1956,15 @@ char *buf, size_t len, size_t *pnread) { struct cf_h2_ctx *ctx = cf->ctx; - struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); + struct h2_stream_ctx *stream; CURLcode result, r2; struct cf_call_data save; + if(!data) + return CURLE_HTTP2; + + stream = H2_STREAM_CTX(ctx, data); + *pnread = 0; if(!stream) { /* Abnormal call sequence: either this transfer has never opened a stream @@ -1986,7 +2007,7 @@ /* pending data to send, need to be called again. Ideally, we * monitor the socket for POLLOUT, but when not SENDING * any more, we force processing of the transfer. */ - if(!CURL_WANT_SEND(data)) + if(!CURL_REQ_WANT_SEND(data)) Curl_multi_mark_dirty(data); } else if(r2) { @@ -2031,7 +2052,7 @@ return CURLE_OK; } /* Server closed before we got a response, this is an error */ - infof(data, "stream %u closed", stream->id); + infof(data, "stream %d closed", stream->id); return CURLE_SEND_ERROR; } @@ -2131,7 +2152,7 @@ } if(stream_id < 0) { - CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", + CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%d", nghttp2_strerror(stream_id), stream_id); result = CURLE_SEND_ERROR; goto out; @@ -2180,7 +2201,7 @@ out: CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu", stream ? stream->id : -1, result, *pnwritten); - Curl_safefree(nva); + curlx_safefree(nva); *pstream = stream; Curl_dynhds_free(&h2_headers); return result; @@ -2231,7 +2252,7 @@ /* if the stream has been closed in egress handling (nghttp2 does that * when it does not like the headers, for example */ if(stream && stream->closed) { - infof(data, "stream %u closed", stream->id); + infof(data, "stream %d closed", stream->id); result = CURLE_SEND_ERROR; goto out; } @@ -2440,7 +2461,7 @@ rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id, data); if(rc) { - infof(data, "http/2: failed to set user_data for stream %u", + infof(data, "http/2: failed to set user_data for stream %d", stream->id); DEBUGASSERT(0); }
diff --git a/Utilities/cmcurl/lib/http2.h b/Utilities/cmcurl/lib/http2.h index a14c848..e38dc57 100644 --- a/Utilities/cmcurl/lib/http2.h +++ b/Utilities/cmcurl/lib/http2.h
@@ -70,7 +70,6 @@ #define Curl_http2_request_upgrade(x, y) CURLE_UNSUPPORTED_PROTOCOL #define Curl_http2_switch(a) CURLE_UNSUPPORTED_PROTOCOL -#define Curl_http2_upgrade(a, b, c, d, e) CURLE_UNSUPPORTED_PROTOCOL #define Curl_h2_http_1_1_error(x) 0 #endif
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c index 492be1a..55efa12 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.c +++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -679,6 +679,10 @@ return compare; } +/* @unittest 1979 */ +UNITTEST CURLcode canon_path(const char *q, size_t len, + struct dynbuf *new_path, + bool do_uri_encode); UNITTEST CURLcode canon_path(const char *q, size_t len, struct dynbuf *new_path, bool do_uri_encode) @@ -705,6 +709,8 @@ return result; } +/* @unittest 1980 */ +UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq); UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq) { CURLcode result = CURLE_OK; @@ -1107,8 +1113,8 @@ Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1], curlx_str(&provider0), curlx_strlen(&provider0)); - curlx_free(data->state.aptr.userpwd); - data->state.aptr.userpwd = auth_headers; + curlx_free(data->req.userpwd); + data->req.userpwd = auth_headers; data->state.authhost.done = TRUE; result = CURLE_OK;
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.h b/Utilities/cmcurl/lib/http_aws_sigv4.h index 25009c6..7dd0024 100644 --- a/Utilities/cmcurl/lib/http_aws_sigv4.h +++ b/Utilities/cmcurl/lib/http_aws_sigv4.h
@@ -31,10 +31,4 @@ /* this is for creating aws_sigv4 header output */ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data); -#ifdef UNITTESTS -UNITTEST CURLcode canon_path(const char *q, size_t len, - struct dynbuf *new_path, bool normalize); -UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq); -#endif - #endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c index db650b1..aa45c79 100644 --- a/Utilities/cmcurl/lib/http_chunks.c +++ b/Utilities/cmcurl/lib/http_chunks.c
@@ -258,12 +258,12 @@ size_t trlen = curlx_dyn_len(&ch->trailer); if(cw_next) result = Curl_cwriter_write(data, cw_next, - CLIENTWRITE_HEADER| + CLIENTWRITE_HEADER | CLIENTWRITE_TRAILER, tr, trlen); else result = Curl_client_write(data, - CLIENTWRITE_HEADER| + CLIENTWRITE_HEADER | CLIENTWRITE_TRAILER, tr, trlen); if(result) {
diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c index f5b20c5..b700707 100644 --- a/Utilities/cmcurl/lib/http_digest.c +++ b/Utilities/cmcurl/lib/http_digest.c
@@ -91,7 +91,7 @@ return CURLE_NOT_BUILT_IN; #else digest = &data->state.proxydigest; - allocuserpwd = &data->state.aptr.proxyuserpwd; + allocuserpwd = &data->req.proxyuserpwd; userp = data->state.aptr.proxyuser; passwdp = data->state.aptr.proxypasswd; authp = &data->state.authproxy; @@ -99,13 +99,13 @@ } else { digest = &data->state.digest; - allocuserpwd = &data->state.aptr.userpwd; + allocuserpwd = &data->req.userpwd; userp = data->state.aptr.user; passwdp = data->state.aptr.passwd; authp = &data->state.authhost; } - Curl_safefree(*allocuserpwd); + curlx_safefree(*allocuserpwd); /* not set means empty */ if(!userp) @@ -125,7 +125,7 @@ return CURLE_OK; } - /* So IE browsers < v7 cut off the URI part at the query part when they + /* IE browsers < v7 cut off the URI part at the query part when they evaluate the MD5 and some (IIS?) servers work with them so we may need to do the Digest IE-style. Note that the different ways cause different MD5 sums to get sent.
diff --git a/Utilities/cmcurl/lib/http_negotiate.c b/Utilities/cmcurl/lib/http_negotiate.c index a6e5c19..74d63d6 100644 --- a/Utilities/cmcurl/lib/http_negotiate.c +++ b/Utilities/cmcurl/lib/http_negotiate.c
@@ -217,13 +217,13 @@ if(proxy) { #ifndef CURL_DISABLE_PROXY - curlx_free(data->state.aptr.proxyuserpwd); - data->state.aptr.proxyuserpwd = userp; + curlx_free(data->req.proxyuserpwd); + data->req.proxyuserpwd = userp; #endif } else { - curlx_free(data->state.aptr.userpwd); - data->state.aptr.userpwd = userp; + curlx_free(data->req.userpwd); + data->req.userpwd = userp; } curlx_free(base64);
diff --git a/Utilities/cmcurl/lib/http_ntlm.c b/Utilities/cmcurl/lib/http_ntlm.c index c4dbdf9..82b0505 100644 --- a/Utilities/cmcurl/lib/http_ntlm.c +++ b/Utilities/cmcurl/lib/http_ntlm.c
@@ -139,7 +139,7 @@ if(proxy) { #ifndef CURL_DISABLE_PROXY - allocuserpwd = &data->state.aptr.proxyuserpwd; + allocuserpwd = &data->req.proxyuserpwd; userp = data->state.aptr.proxyuser; passwdp = data->state.aptr.proxypasswd; service = data->set.str[STRING_PROXY_SERVICE_NAME] ? @@ -152,7 +152,7 @@ #endif } else { - allocuserpwd = &data->state.aptr.userpwd; + allocuserpwd = &data->req.userpwd; userp = data->state.aptr.user; passwdp = data->state.aptr.passwd; service = data->set.str[STRING_SERVICE_NAME] ? @@ -176,9 +176,9 @@ #ifdef USE_WINDOWS_SSPI if(!Curl_pSecFn) { /* not thread-safe and leaks - use curl_global_init() to avoid */ - CURLcode err = Curl_sspi_global_init(); + result = Curl_sspi_global_init(); if(!Curl_pSecFn) - return err; + return result; } #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS ntlm->sslContext = conn->sslContext; @@ -242,7 +242,7 @@ data->info.proxyauthpicked = CURLAUTH_NTLM; else data->info.httpauthpicked = CURLAUTH_NTLM; - Curl_safefree(*allocuserpwd); + curlx_safefree(*allocuserpwd); authp->done = TRUE; break; }
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c index a9521c9..a4bdd7e 100644 --- a/Utilities/cmcurl/lib/http_proxy.c +++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -164,7 +164,7 @@ void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, const char **phostname, - int *pport, bool *pipv6_ip) + uint16_t *pport, bool *pipv6_ip) { DEBUGASSERT(cf); DEBUGASSERT(cf->conn); @@ -183,10 +183,7 @@ else *pport = cf->conn->remote_port; - if(*phostname != cf->conn->host.name) - *pipv6_ip = (strchr(*phostname, ':') != NULL); - else - *pipv6_ip = (bool)cf->conn->bits.ipv6_ip; + *pipv6_ip = (strchr(*phostname, ':') != NULL); } struct cf_proxy_ctx { @@ -202,14 +199,14 @@ struct cf_proxy_ctx *ctx = cf->ctx; const char *hostname = NULL; char *authority = NULL; - int port; + uint16_t port; bool ipv6_ip; CURLcode result; struct httpreq *req = NULL; Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); - authority = curl_maprintf("%s%s%s:%d", ipv6_ip ? "[" : "", hostname, + authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname, ipv6_ip ? "]" : "", port); if(!authority) { result = CURLE_OUT_OF_MEMORY; @@ -236,9 +233,9 @@ goto out; } - if(data->state.aptr.proxyuserpwd) { + if(data->req.proxyuserpwd) { result = Curl_dynhds_h1_cadd_line(&req->headers, - data->state.aptr.proxyuserpwd); + data->req.proxyuserpwd); if(result) goto out; } @@ -394,7 +391,7 @@ struct Curl_cftype Curl_cft_http_proxy = { "HTTP-PROXY", - CF_TYPE_IP_CONNECT | CF_TYPE_PROXY, + CF_TYPE_IP_CONNECT | CF_TYPE_PROXY | CF_TYPE_SETUP, 0, http_proxy_cf_destroy, http_proxy_cf_connect,
diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h index c27787b..155b222 100644 --- a/Utilities/cmcurl/lib/http_proxy.h +++ b/Utilities/cmcurl/lib/http_proxy.h
@@ -37,7 +37,7 @@ void Curl_http_proxy_get_destination(struct Curl_cfilter *cf, const char **phostname, - int *pport, bool *pipv6_ip); + uint16_t *pport, bool *pipv6_ip); CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, struct Curl_cfilter *cf,
diff --git a/Utilities/cmcurl/lib/httpsrr.c b/Utilities/cmcurl/lib/httpsrr.c index 169a0b8..90fcb52 100644 --- a/Utilities/cmcurl/lib/httpsrr.c +++ b/Utilities/cmcurl/lib/httpsrr.c
@@ -30,6 +30,7 @@ #include "connect.h" #include "curl_trc.h" #include "curlx/strdup.h" +#include "curlx/inet_ntop.h" static CURLcode httpsrr_decode_alpn(const uint8_t *cp, size_t len, unsigned char *alpns) @@ -68,71 +69,164 @@ return CURLE_OK; } -CURLcode Curl_httpsrr_set(struct Curl_easy *data, - struct Curl_https_rrinfo *hi, +#ifdef CURLVERBOSE + +static CURLcode httpsrr_print_addr(struct dynbuf *dyn, + int ai_family, + const uint8_t *addr, + size_t total_len) +{ + char buf[MAX_IPADR_LEN]; + size_t i, alen = (ai_family == AF_INET6) ? 16 : 4; + const char *sep = ""; + CURLcode result = CURLE_OK; + + for(i = 0; (i < (total_len / alen)) && !result; ++i) { + if(!curlx_inet_ntop(ai_family, addr + (i * alen), buf, sizeof(buf))) + result = curlx_dyn_add(dyn, "<error parsing address>"); + else + result = curlx_dyn_addf(dyn, "%s%s", sep, buf); + sep = ","; + } + return result; +} + +void Curl_httpsrr_trace(struct Curl_easy *data, + struct Curl_https_rrinfo *rr) +{ + struct dynbuf tmp; + CURLcode result; + + if(!rr || !rr->complete) { + CURL_TRC_DNS(data, "[HTTPS-RR] not available"); + return; + } + curlx_dyn_init(&tmp, 1024); + result = Curl_httpsrr_print(&tmp, rr); + if(!result) + CURL_TRC_DNS(data, "HTTPS-RR: %s", curlx_dyn_ptr(&tmp)); + else + CURL_TRC_DNS(data, "Error printing HTTPS-RR information"); + curlx_dyn_free(&tmp); +} + +CURLcode Curl_httpsrr_print(struct dynbuf *tmp, + struct Curl_https_rrinfo *rr) +{ + CURLcode result; + int i; + + curlx_dyn_reset(tmp); + result = curlx_dyn_addf(tmp, "%u %s", rr->priority, + rr->target ? rr->target : "."); + if(!result && rr->mandatory) + result = curlx_dyn_add(tmp, " mandatory-keys(ignored)"); + if(!result && rr->alpns[0]) { + const char *sep = "", *name; + result = curlx_dyn_add(tmp, " alpn="); + for(i = 0; !result && (i < 4); ++i) { + switch(rr->alpns[i]) { + case ALPN_h1: + name = "http/1.1"; + break; + case ALPN_h2: + name = "h2"; + break; + case ALPN_h3: + name = "h3"; + break; + default: + name = NULL; + } + if(name) { + result = curlx_dyn_addf(tmp, "%s%s", sep, name); + sep = ","; + } + } + } + if(!result && rr->port_set) { + result = curlx_dyn_addf(tmp, " port=%u", rr->port); + } + if(!result && rr->no_def_alpn) + result = curlx_dyn_add(tmp, " no-default-alpn"); + if(!result && rr->ipv6hints_len) { + result = curlx_dyn_add(tmp, " ipv6hint="); + if(!result) + result = httpsrr_print_addr( + tmp, AF_INET6, rr->ipv6hints, rr->ipv6hints_len); + } + if(!result && rr->ipv4hints_len) { + result = curlx_dyn_add(tmp, " ipv4hint="); + if(!result) + result = httpsrr_print_addr( + tmp, AF_INET, rr->ipv4hints, rr->ipv4hints_len); + } + if(!result && rr->echconfiglist_len) + result = curlx_dyn_addf(tmp, " ech=<%zu bytes>", rr->echconfiglist_len); + + return result; +} + +#endif /* CURLVERBOSE */ + +CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, uint16_t rrkey, const uint8_t *val, size_t vlen) { CURLcode result = CURLE_OK; switch(rrkey) { case HTTPS_RR_CODE_MANDATORY: - CURL_TRC_DNS(data, "HTTPS RR MANDATORY left to implement"); + rr->mandatory = TRUE; break; case HTTPS_RR_CODE_ALPN: /* str_list */ - result = httpsrr_decode_alpn(val, vlen, hi->alpns); - CURL_TRC_DNS(data, "HTTPS RR ALPN: %u %u %u %u", - hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]); + result = httpsrr_decode_alpn(val, vlen, rr->alpns); break; case HTTPS_RR_CODE_NO_DEF_ALPN: if(vlen) /* no data */ return CURLE_BAD_FUNCTION_ARGUMENT; - hi->no_def_alpn = TRUE; - CURL_TRC_DNS(data, "HTTPS RR no-def-alpn"); + rr->no_def_alpn = TRUE; break; case HTTPS_RR_CODE_IPV4: /* addr4 list */ if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */ return CURLE_BAD_FUNCTION_ARGUMENT; - curlx_free(hi->ipv4hints); - hi->ipv4hints = curlx_memdup(val, vlen); - if(!hi->ipv4hints) + curlx_free(rr->ipv4hints); + rr->ipv4hints = curlx_memdup(val, vlen); + if(!rr->ipv4hints) return CURLE_OUT_OF_MEMORY; - hi->ipv4hints_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR IPv4"); + rr->ipv4hints_len = vlen; break; case HTTPS_RR_CODE_ECH: if(!vlen) return CURLE_BAD_FUNCTION_ARGUMENT; - curlx_free(hi->echconfiglist); - hi->echconfiglist = curlx_memdup(val, vlen); - if(!hi->echconfiglist) + curlx_free(rr->echconfiglist); + rr->echconfiglist = curlx_memdup(val, vlen); + if(!rr->echconfiglist) return CURLE_OUT_OF_MEMORY; - hi->echconfiglist_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR ECH"); + rr->echconfiglist_len = vlen; break; case HTTPS_RR_CODE_IPV6: /* addr6 list */ if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */ return CURLE_BAD_FUNCTION_ARGUMENT; - curlx_free(hi->ipv6hints); - hi->ipv6hints = curlx_memdup(val, vlen); - if(!hi->ipv6hints) + curlx_free(rr->ipv6hints); + rr->ipv6hints = curlx_memdup(val, vlen); + if(!rr->ipv6hints) return CURLE_OUT_OF_MEMORY; - hi->ipv6hints_len = vlen; - CURL_TRC_DNS(data, "HTTPS RR IPv6"); + rr->ipv6hints_len = vlen; break; case HTTPS_RR_CODE_PORT: if(vlen != 2) return CURLE_BAD_FUNCTION_ARGUMENT; - hi->port = (unsigned short)((val[0] << 8) | val[1]); - CURL_TRC_DNS(data, "HTTPS RR port %u", hi->port); + rr->port = (uint16_t)((val[0] << 8) | val[1]); + rr->port_set = TRUE; break; default: - CURL_TRC_DNS(data, "HTTPS RR unknown code"); + /* unknown code */ break; } return result; } -struct Curl_https_rrinfo * -Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo) +struct Curl_https_rrinfo *Curl_httpsrr_dup_move( + struct Curl_https_rrinfo *rrinfo) { struct Curl_https_rrinfo *dup = curlx_memdup(rrinfo, sizeof(*rrinfo)); if(dup) @@ -142,17 +236,27 @@ void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo) { - Curl_safefree(rrinfo->target); - Curl_safefree(rrinfo->echconfiglist); - Curl_safefree(rrinfo->ipv4hints); - Curl_safefree(rrinfo->ipv6hints); - Curl_safefree(rrinfo->rrname); + curlx_safefree(rrinfo->target); + curlx_safefree(rrinfo->echconfiglist); + curlx_safefree(rrinfo->ipv4hints); + curlx_safefree(rrinfo->ipv6hints); + curlx_safefree(rrinfo->rrname); + rrinfo->complete = FALSE; +} + +bool Curl_httpsrr_applicable(struct Curl_easy *data, + const struct Curl_https_rrinfo *rr) +{ + if(!data->conn || !rr) + return FALSE; + return (!rr->target || !rr->target[0] || + (rr->target[0] == '.' && !rr->target[1])) && + (!rr->port_set || rr->port == data->conn->remote_port); } #ifdef USE_ARES -static CURLcode httpsrr_opt(struct Curl_easy *data, - const ares_dns_rr_t *rr, +static CURLcode httpsrr_opt(const ares_dns_rr_t *rr, ares_dns_rr_key_t key, size_t idx, struct Curl_https_rrinfo *hinfo) { @@ -161,11 +265,10 @@ size_t len = 0; code = ares_dns_rr_get_opt(rr, key, idx, &val, &len); - return Curl_httpsrr_set(data, hinfo, code, val, len); + return Curl_httpsrr_set(hinfo, code, val, len); } -CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, - const ares_dns_record_t *dnsrec, +CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, struct Curl_https_rrinfo *hinfo) { CURLcode result = CURLE_OK; @@ -188,19 +291,18 @@ result = CURLE_OUT_OF_MEMORY; goto out; } - CURL_TRC_DNS(data, "HTTPS RR target: %s", hinfo->target); } - CURL_TRC_DNS(data, "HTTPS RR priority: %u", - ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY)); + hinfo->priority = ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY); for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS); opt++) { - result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); + result = httpsrr_opt(rr, ARES_RR_HTTPS_PARAMS, opt, hinfo); if(result) break; } } out: - Curl_safefree(hinfo->rrname); + hinfo->complete = !result; + curlx_safefree(hinfo->rrname); return result; }
diff --git a/Utilities/cmcurl/lib/httpsrr.h b/Utilities/cmcurl/lib/httpsrr.h index 8fe7c0c..28a790d 100644 --- a/Utilities/cmcurl/lib/httpsrr.h +++ b/Utilities/cmcurl/lib/httpsrr.h
@@ -35,6 +35,7 @@ #define MAX_HTTPSRR_ALPNS 4 struct Curl_easy; +struct dynbuf; struct Curl_https_rrinfo { char *rrname; /* if NULL, the same as the URL hostname */ @@ -51,20 +52,26 @@ size_t ipv6hints_len; unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */ /* store parsed alpnid entries in the array, end with ALPN_none */ - int port; /* -1 means not set */ + uint16_t port; uint16_t priority; BIT(no_def_alpn); /* keytag = 2 */ + BIT(mandatory); /* keytag = 0 */ + BIT(port_set); /* port value has been assigned */ + BIT(complete); /* values have been successfully assigned */ }; -CURLcode Curl_httpsrr_set(struct Curl_easy *data, - struct Curl_https_rrinfo *hi, +CURLcode Curl_httpsrr_set(struct Curl_https_rrinfo *rr, uint16_t rrkey, const uint8_t *val, size_t vlen); -struct Curl_https_rrinfo * -Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo); +struct Curl_https_rrinfo *Curl_httpsrr_dup_move( + struct Curl_https_rrinfo *rrinfo); void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo); +/* TRUE if the record is applicable to the transfer and its connection. */ +bool Curl_httpsrr_applicable(struct Curl_easy *data, + const struct Curl_https_rrinfo *rr); + /* * Code points for DNS wire format SvcParams as per RFC 9460 */ @@ -77,10 +84,19 @@ #define HTTPS_RR_CODE_IPV6 0x06 #ifdef USE_ARES -CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data, - const ares_dns_record_t *dnsrec, +CURLcode Curl_httpsrr_from_ares(const ares_dns_record_t *dnsrec, struct Curl_https_rrinfo *hinfo); #endif /* USE_ARES */ + +#ifdef CURLVERBOSE +CURLcode Curl_httpsrr_print(struct dynbuf *tmp, + struct Curl_https_rrinfo *rr); +void Curl_httpsrr_trace(struct Curl_easy *data, + struct Curl_https_rrinfo *rr); +#else +#define Curl_httpsrr_trace(a, b) Curl_nop_stmt +#endif + #endif /* USE_HTTPSRR */ #endif /* HEADER_CURL_HTTPSRR_H */
diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c index 2db4f6d..f2b954e 100644 --- a/Utilities/cmcurl/lib/idn.c +++ b/Utilities/cmcurl/lib/idn.c
@@ -33,10 +33,10 @@ #include <idn2.h> #if defined(_WIN32) && defined(UNICODE) -#define IDN2_LOOKUP(name, host, flags) \ +#define IDN2_LOOKUP(name, host, flags) \ idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags) #else -#define IDN2_LOOKUP(name, host, flags) \ +#define IDN2_LOOKUP(name, host, flags) \ idn2_lookup_ul((const char *)(name), (char **)(host), flags) #endif #endif /* USE_LIBIDN2 */ @@ -348,7 +348,7 @@ */ void Curl_free_idnconverted_hostname(struct hostname *host) { - Curl_safefree(host->encalloc); + curlx_safefree(host->encalloc); } #endif /* USE_IDN */
diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c index 05e3f84..b71254a 100644 --- a/Utilities/cmcurl/lib/if2ip.c +++ b/Utilities/cmcurl/lib/if2ip.c
@@ -210,13 +210,13 @@ memcpy(req.ifr_name, interf, len + 1); req.ifr_addr.sa_family = AF_INET; -#if defined(__GNUC__) && defined(_AIX) +#if defined(CURL_HAVE_DIAG) && defined(_AIX) /* Suppress warning inside system headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wshift-sign-overflow" #endif if(ioctl(dummy, SIOCGIFADDR, &req) < 0) { -#if defined(__GNUC__) && defined(_AIX) +#if defined(CURL_HAVE_DIAG) && defined(_AIX) #pragma GCC diagnostic pop #endif sclose(dummy);
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c index 0546595..5ef2a2c 100644 --- a/Utilities/cmcurl/lib/imap.c +++ b/Utilities/cmcurl/lib/imap.c
@@ -775,7 +775,8 @@ char *mailbox; /* Invalidate old information as we are switching mailboxes */ - Curl_safefree(imapc->mailbox); + curlx_safefree(imapc->mailbox); + imapc->mb_uidvalidity_set = FALSE; /* Check we have a mailbox */ if(!imap->mailbox) { @@ -1024,7 +1025,6 @@ imapstate instate) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf); (void)instate; @@ -1076,7 +1076,7 @@ line += wordlen; } } - else if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) { + else if(data->set.use_ssl && !Curl_xfer_is_secure(data)) { /* PREAUTH is not compatible with STARTTLS. */ if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) { /* Switch to TLS connection now */ @@ -1452,8 +1452,8 @@ if(result) return result; - infof(data, "Written %zu bytes, %" FMT_OFF_TU - " bytes are left for transfer", chunk, size - chunk); + infof(data, "Written %zu bytes, %" FMT_OFF_T + " bytes are left for transfer", chunk, (curl_off_t)(size - chunk)); /* Have we used the entire overflow or part of it?*/ if(pp->overflow > chunk) { @@ -1696,14 +1696,15 @@ static void imap_easy_reset(struct IMAP *imap) { - Curl_safefree(imap->mailbox); - Curl_safefree(imap->uid); - Curl_safefree(imap->mindex); - Curl_safefree(imap->section); - Curl_safefree(imap->partial); - Curl_safefree(imap->query); - Curl_safefree(imap->custom); - Curl_safefree(imap->custom_params); + curlx_safefree(imap->mailbox); + curlx_safefree(imap->uid); + curlx_safefree(imap->mindex); + curlx_safefree(imap->section); + curlx_safefree(imap->partial); + curlx_safefree(imap->query); + curlx_safefree(imap->custom); + curlx_safefree(imap->custom_params); + imap->uidvalidity_set = FALSE; /* Clear the transfer mode for the next request */ imap->transfer = PPTRANSFER_BODY; } @@ -2249,7 +2250,7 @@ (void)klen; Curl_pp_disconnect(&imapc->pp); curlx_dyn_free(&imapc->dyn); - Curl_safefree(imapc->mailbox); + curlx_safefree(imapc->mailbox); curlx_free(imapc); } @@ -2302,7 +2303,7 @@ /* * IMAP protocol. */ -static const struct Curl_protocol Curl_protocol_imap = { +const struct Curl_protocol Curl_protocol_imap = { imap_setup_connection, /* setup_connection */ imap_do, /* do_it */ imap_done, /* done */ @@ -2317,44 +2318,9 @@ imap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif /* CURL_DISABLE_IMAP */ - -/* - * IMAP protocol handler. - */ -const struct Curl_scheme Curl_scheme_imap = { - "imap", /* scheme */ -#ifdef CURL_DISABLE_IMAP - ZERO_NULL, -#else - &Curl_protocol_imap, -#endif - CURLPROTO_IMAP, /* protocol */ - CURLPROTO_IMAP, /* family */ - PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | - PROTOPT_CONN_REUSE, - PORT_IMAP, /* defport */ -}; - -/* - * IMAPS protocol handler. - */ -const struct Curl_scheme Curl_scheme_imaps = { - "imaps", /* scheme */ -#if defined(CURL_DISABLE_IMAP) || !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_imap, -#endif - CURLPROTO_IMAPS, /* protocol */ - CURLPROTO_IMAP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, - PORT_IMAPS, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/imap.h b/Utilities/cmcurl/lib/imap.h index f179991..ecfb1ec 100644 --- a/Utilities/cmcurl/lib/imap.h +++ b/Utilities/cmcurl/lib/imap.h
@@ -23,8 +23,6 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_imap; -extern const struct Curl_scheme Curl_scheme_imaps; /* Authentication type flags */ #define IMAP_TYPE_CLEARTEXT (1 << 0) @@ -34,4 +32,8 @@ #define IMAP_TYPE_NONE 0 #define IMAP_TYPE_ANY (IMAP_TYPE_CLEARTEXT | IMAP_TYPE_SASL) +#ifndef CURL_DISABLE_IMAP +extern const struct Curl_protocol Curl_protocol_imap; +#endif + #endif /* HEADER_CURL_IMAP_H */
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c index e223078..16c93ee 100644 --- a/Utilities/cmcurl/lib/ldap.c +++ b/Utilities/cmcurl/lib/ldap.c
@@ -27,7 +27,7 @@ #if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP) -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -115,7 +115,7 @@ static curl_ldap_num_t ldap_url_parse_low(struct Curl_easy *data, const struct connectdata *conn, - LDAPURLDesc **ludp); + LDAPURLDesc **ludpp); static void ldap_free_urldesc_low(LDAPURLDesc *ludp); #undef ldap_free_urldesc @@ -185,8 +185,8 @@ } if(method && user && passwd) { - CURLcode res = Curl_create_sspi_identity(user, passwd, &cred); - if(!res) { + CURLcode result = Curl_create_sspi_identity(user, passwd, &cred); + if(!result) { rc = ldap_bind_s(server, NULL, (TCHAR *)&cred, method); Curl_sspi_free_identity(&cred); } @@ -303,9 +303,8 @@ if(ldap_ssl) server = ldap_sslinit(host, (curl_ldap_num_t)ipquad.remote_port, 1); else -#else - server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); #endif + server = ldap_init(host, (curl_ldap_num_t)ipquad.remote_port); if(!server) { failf(data, "LDAP: cannot setup connect to %s:%u", conn->host.dispname, ipquad.remote_port); @@ -388,7 +387,6 @@ } #ifdef USE_WIN32_LDAP - ldap_set_option(server, LDAP_OPT_PROTOCOL_VERSION, &ldap_proto); rc = ldap_win_bind(data, server, user, passwd); #else rc = ldap_simple_bind_s(server, user, passwd); @@ -991,45 +989,13 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif #endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */ - -/* - * LDAP - */ -const struct Curl_scheme Curl_scheme_ldap = { - "ldap", /* scheme */ -#ifdef CURL_DISABLE_LDAP - ZERO_NULL, -#else - &Curl_protocol_ldap, -#endif - CURLPROTO_LDAP, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL_REUSE, /* flags */ - PORT_LDAP, /* defport */ -}; - -/* - * LDAPS - */ -const struct Curl_scheme Curl_scheme_ldaps = { - "ldaps", /* scheme */ -#if defined(CURL_DISABLE_LDAP) || !defined(HAVE_LDAP_SSL) - ZERO_NULL, -#else - &Curl_protocol_ldap, -#endif - CURLPROTO_LDAPS, /* protocol */ - CURLPROTO_LDAP, /* family */ - PROTOPT_SSL, /* flags */ - PORT_LDAPS, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/llist.c b/Utilities/cmcurl/lib/llist.c index 0e3084b..3ec85e4 100644 --- a/Utilities/cmcurl/lib/llist.c +++ b/Utilities/cmcurl/lib/llist.c
@@ -40,7 +40,7 @@ #define VERIFYNODE(x) x #endif /* - * @unittest: 1300 + * @unittest 1300 */ void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor) { @@ -62,7 +62,7 @@ * * The 'ne' argument should be a pointer into the object to store. * - * @unittest: 1300 + * @unittest 1300 */ void Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_node *e, /* may be NULL */ @@ -112,7 +112,7 @@ * * The 'ne' argument should be a pointer into the object to store. * - * @unittest: 1300 + * @unittest 1300 */ void Curl_llist_append(struct Curl_llist *list, const void *p, struct Curl_llist_node *ne) @@ -168,11 +168,7 @@ return ptr; } -/* - * @unittest: 1300 - */ -UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user); -UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user) +static void node_uremove(struct Curl_llist_node *e, void *user) { struct Curl_llist *list; void *ptr; @@ -190,7 +186,7 @@ void Curl_node_remove(struct Curl_llist_node *e) { - Curl_node_uremove(e, NULL); + node_uremove(e, NULL); } void Curl_llist_destroy(struct Curl_llist *list, void *user) @@ -198,7 +194,7 @@ if(list) { DEBUGASSERT(list->_init == LLISTINIT); while(list->_size > 0) - Curl_node_uremove(list->_tail, user); + node_uremove(list->_tail, user); } } @@ -212,9 +208,13 @@ } #ifdef UNITTESTS -/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which - might be NULL */ -struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list) +/* llist_tail() returns the last 'struct Curl_llist_node *', which might be + NULL + + @unittest 1300 +*/ +UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list); +UNITTEST struct Curl_llist_node *llist_tail(struct Curl_llist *list) { DEBUGASSERT(list); DEBUGASSERT(list->_init == LLISTINIT); @@ -248,16 +248,18 @@ } #ifdef UNITTESTS +/* llist_node_prev() returns the previous element in a list from a given + Curl_llist_node -/* Curl_node_prev() returns the previous element in a list from a given - Curl_llist_node */ -struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n) + @unittest 1300 +*/ +UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n); +UNITTEST struct Curl_llist_node *llist_node_prev(struct Curl_llist_node *n) { DEBUGASSERT(n); DEBUGASSERT(n->_init == NODEINIT); return VERIFYNODE(n->_prev); } - #endif struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n)
diff --git a/Utilities/cmcurl/lib/llist.h b/Utilities/cmcurl/lib/llist.h index 09c695d..28e958d 100644 --- a/Utilities/cmcurl/lib/llist.h +++ b/Utilities/cmcurl/lib/llist.h
@@ -62,10 +62,6 @@ might be NULL */ struct Curl_llist_node *Curl_llist_head(struct Curl_llist *list); -/* Curl_llist_tail() returns the last 'struct Curl_llist_node *', which - might be NULL */ -struct Curl_llist_node *Curl_llist_tail(struct Curl_llist *list); - /* Curl_llist_count() returns a size_t the number of nodes in the list */ size_t Curl_llist_count(struct Curl_llist *list); @@ -73,17 +69,13 @@ void *Curl_node_elem(struct Curl_llist_node *n); /* Remove the node from the list and return the custom data - * from a Curl_llist_node. Will NOT invoke a registered `dtor`. */ + * from a Curl_llist_node. Does NOT invoke a registered `dtor`. */ void *Curl_node_take_elem(struct Curl_llist_node *e); /* Curl_node_next() returns the next element in a list from a given Curl_llist_node */ struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n); -/* Curl_node_prev() returns the previous element in a list from a given - Curl_llist_node */ -struct Curl_llist_node *Curl_node_prev(struct Curl_llist_node *n); - /* Curl_node_llist() return the list the node is in or NULL. */ struct Curl_llist *Curl_node_llist(struct Curl_llist_node *n);
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c index 1ac6ef1..0213483 100644 --- a/Utilities/cmcurl/lib/md4.c +++ b/Utilities/cmcurl/lib/md4.c
@@ -33,7 +33,7 @@ /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */ #define OPENSSL_NO_MD4 #else -/* Cover also OPENSSL_NO_MD4 configured in openssl */ +/* Cover also OPENSSL_NO_MD4 configured in OpenSSL */ #include <openssl/opensslconf.h> #endif #endif /* USE_OPENSSL */ @@ -44,19 +44,41 @@ /* When OpenSSL or wolfSSL is available, we use their MD4 functions. */ -#if defined(USE_WOLFSSL) && !defined(NO_MD4) -#include <wolfssl/openssl/md4.h> -#define VOID_MD4_INIT +#if defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) +#include <openssl/md4.h> -#ifdef OPENSSL_COEXIST -# define MD4_CTX WOLFSSL_MD4_CTX -# define MD4_Init wolfSSL_MD4_Init -# define MD4_Update wolfSSL_MD4_Update -# define MD4_Final wolfSSL_MD4_Final +/* Map directly to OpenSSL implementation */ +#define my_md4_ctx MD4_CTX +#define my_md4_init MD4_Init +#define my_md4_update MD4_Update +#define my_md4_final MD4_Final + +#elif defined(USE_WOLFSSL) && !defined(NO_MD4) +#include <wolfssl/wolfcrypt/md4.h> +#include <wolfssl/version.h> + +#if LIBWOLFSSL_VERSION_HEX >= 0x05007006 +typedef wc_Md4 my_md4_ctx; +#else +typedef Md4 my_md4_ctx; #endif -#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4) -#include <openssl/md4.h> +static int my_md4_init(my_md4_ctx *ctx) +{ + wc_InitMd4(ctx); + return 1; +} + +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) +{ + wc_Md4Update(ctx, input, (word32)len); +} + +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) +{ + wc_Md4Final(ctx, digest); +} #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040) && \ @@ -68,19 +90,20 @@ (__IPHONE_OS_VERSION_MIN_REQUIRED < 130000)) #include <CommonCrypto/CommonDigest.h> -typedef CC_MD4_CTX MD4_CTX; +typedef CC_MD4_CTX my_md4_ctx; -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { return CC_MD4_Init(ctx); } -static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { (void)CC_MD4_Update(ctx, input, (CC_LONG)len); } -static void MD4_Final(unsigned char *digest, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { (void)CC_MD4_Final(digest, ctx); } @@ -92,9 +115,9 @@ HCRYPTPROV hCryptProv; HCRYPTHASH hHash; }; -typedef struct md4_ctx MD4_CTX; +typedef struct md4_ctx my_md4_ctx; -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { ctx->hCryptProv = 0; ctx->hHash = 0; @@ -112,12 +135,13 @@ return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { CryptHashData(ctx->hHash, (const BYTE *)input, (unsigned int)len, 0); } -static void MD4_Final(unsigned char *digest, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { unsigned long length = 0; @@ -135,20 +159,21 @@ #elif defined(USE_GNUTLS) #include <nettle/md4.h> -typedef struct md4_ctx MD4_CTX; +typedef struct md4_ctx my_md4_ctx; -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { md4_init(ctx); return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { md4_update(ctx, len, input); } -static void MD4_Final(unsigned char *digest, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { md4_digest(ctx, MD4_DIGEST_SIZE, digest); } @@ -187,7 +212,7 @@ unsigned char buffer[64]; uint32_t block[16]; }; -typedef struct md4_ctx MD4_CTX; +typedef struct md4_ctx my_md4_ctx; /* * The basic MD4 functions. @@ -229,7 +254,7 @@ * This processes one or more 64-byte data blocks, but does NOT update * the bit counters. There are no alignment requirements. */ -static const void *my_md4_body(MD4_CTX *ctx, +static const void *my_md4_body(my_md4_ctx *ctx, const void *input, unsigned long size) { const unsigned char *ptr; @@ -320,7 +345,7 @@ return ptr; } -static int MD4_Init(MD4_CTX *ctx) +static int my_md4_init(my_md4_ctx *ctx) { ctx->a = 0x67452301; ctx->b = 0xefcdab89; @@ -333,7 +358,8 @@ return 1; } -static void MD4_Update(MD4_CTX *ctx, const void *input, unsigned long len) +static void my_md4_update(my_md4_ctx *ctx, + const void *input, unsigned long len) { uint32_t saved_lo; unsigned long used; @@ -368,7 +394,7 @@ memcpy(ctx->buffer, input, len); } -static void MD4_Final(unsigned char *digest, MD4_CTX *ctx) +static void my_md4_final(unsigned char *digest, my_md4_ctx *ctx) { unsigned long used, available; @@ -424,17 +450,11 @@ CURLcode Curl_md4it(unsigned char *output, const unsigned char *input, const size_t len) { - MD4_CTX ctx; - -#ifdef VOID_MD4_INIT - MD4_Init(&ctx); -#else - if(!MD4_Init(&ctx)) + my_md4_ctx ctx; + if(!my_md4_init(&ctx)) return CURLE_FAILED_INIT; -#endif - - MD4_Update(&ctx, input, curlx_uztoui(len)); - MD4_Final(output, &ctx); + my_md4_update(&ctx, input, curlx_uztoui(len)); + my_md4_final(output, &ctx); return CURLE_OK; }
diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c index c2bd176..4dd0d7c 100644 --- a/Utilities/cmcurl/lib/md5.c +++ b/Utilities/cmcurl/lib/md5.c
@@ -67,14 +67,9 @@ md5_digest(ctx, 16, digest); } -#elif (defined(USE_OPENSSL) && \ - !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0)) || \ - (defined(USE_WOLFSSL) && !defined(NO_MD5) && !defined(OPENSSL_COEXIST)) -#ifdef USE_OPENSSL +#elif defined(USE_OPENSSL) && \ + !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0) #include <openssl/md5.h> -#else -#include <wolfssl/openssl/md5.h> -#endif typedef MD5_CTX my_md5_ctx; @@ -98,27 +93,26 @@ } #elif defined(USE_WOLFSSL) && !defined(NO_MD5) -#include <wolfssl/openssl/md5.h> +#include <wolfssl/wolfcrypt/md5.h> -typedef WOLFSSL_MD5_CTX my_md5_ctx; +typedef wc_Md5 my_md5_ctx; static CURLcode my_md5_init(void *ctx) { - if(!wolfSSL_MD5_Init(ctx)) + if(wc_InitMd5(ctx)) return CURLE_OUT_OF_MEMORY; - return CURLE_OK; } static void my_md5_update(void *ctx, const unsigned char *input, unsigned int len) { - (void)wolfSSL_MD5_Update(ctx, input, len); + (void)wc_Md5Update(ctx, input, (word32)len); } static void my_md5_final(unsigned char *digest, void *ctx) { - (void)wolfSSL_MD5_Final(digest, ctx); + (void)wc_Md5Final(ctx, digest); } #elif defined(USE_MBEDTLS) && \ @@ -546,14 +540,19 @@ * Returns CURLE_OK on success. */ CURLcode Curl_md5it(unsigned char *output, - const unsigned char *input, const size_t len) + const unsigned char *input, size_t len) { CURLcode result; my_md5_ctx ctx; result = my_md5_init(&ctx); if(!result) { - my_md5_update(&ctx, input, curlx_uztoui(len)); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + my_md5_update(&ctx, input, ilen); + input += ilen; + len -= ilen; + } while(len); my_md5_final(output, &ctx); } return result;
diff --git a/Utilities/cmcurl/lib/memdebug.c b/Utilities/cmcurl/lib/memdebug.c index af4fc9d..6eda7a8 100644 --- a/Utilities/cmcurl/lib/memdebug.c +++ b/Utilities/cmcurl/lib/memdebug.c
@@ -65,14 +65,14 @@ static char membuf[10000]; static size_t memwidx = 0; /* write index */ -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_MUTEX static bool dbg_mutex_init = 0; static curl_mutex_t dbg_mutex; #endif static bool curl_dbg_lock(void) { -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_MUTEX if(dbg_mutex_init) { Curl_mutex_acquire(&dbg_mutex); return TRUE; @@ -83,7 +83,7 @@ static void curl_dbg_unlock(bool was_locked) { -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_MUTEX if(was_locked) Curl_mutex_release(&dbg_mutex); #else @@ -99,6 +99,7 @@ _exit() comes after the atexit handlers are called. curl/curl#6620 */ static void curl_dbg_cleanup(void) { + bool locked = curl_dbg_lock(); if(curl_dbg_logfile && curl_dbg_logfile != stderr && curl_dbg_logfile != stdout) { @@ -108,7 +109,8 @@ fclose(curl_dbg_logfile); } curl_dbg_logfile = NULL; -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) + curl_dbg_unlock(locked); +#ifdef USE_MUTEX if(dbg_mutex_init) { Curl_mutex_destroy(&dbg_mutex); dbg_mutex_init = FALSE; @@ -157,7 +159,7 @@ setbuf(curl_dbg_logfile, (char *)NULL); #endif } -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) +#ifdef USE_MUTEX if(!dbg_mutex_init) { dbg_mutex_init = TRUE; Curl_mutex_init(&dbg_mutex);
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c index 37ea514..b7e51aa 100644 --- a/Utilities/cmcurl/lib/mime.c +++ b/Utilities/cmcurl/lib/mime.c
@@ -44,11 +44,14 @@ #include "slist.h" #include "curlx/dynbuf.h" +#define MAX_MIME_LEVELS 40 /* avoid deep nesting */ + #define READ_ERROR ((size_t)-1) #define STOP_FILLING ((size_t)-2) static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, - void *instream, bool *hasread); + void *instream, bool *hasread, + size_t call_depth); static curl_off_t mime_size(curl_mimepart *part); /* Quoted-printable character class table. @@ -162,19 +165,20 @@ static FILE *vmsfopenread(const char *file, const char *mode) { curlx_struct_stat statbuf; - int result; + int res = curlx_stat(file, &statbuf); - result = curlx_stat(file, &statbuf); - - switch(statbuf.st_fab_rfm) { - case FAB$C_VAR: - case FAB$C_VFC: - case FAB$C_STMCR: - return curlx_fopen(file, FOPEN_READTEXT); /* VMS */ - break; - default: - return curlx_fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm"); + if(res != -1) { + switch(statbuf.st_fab_rfm) { + case FAB$C_VAR: + case FAB$C_VFC: + case FAB$C_STMCR: + return curlx_fopen(file, FOPEN_READTEXT); /* VMS */ + break; + default: + return curlx_fopen(file, FOPEN_READTEXT, "rfm=stmlf", "ctx=stm"); + } } + return NULL; } #define fopen_read vmsfopenread @@ -599,7 +603,7 @@ static void mime_mem_free(void *ptr) { - Curl_safefree(((curl_mimepart *)ptr)->data); + curlx_safefree(((curl_mimepart *)ptr)->data); } /* Named file callbacks. */ @@ -650,7 +654,7 @@ curlx_fclose(part->fp); part->fp = NULL; } - Curl_safefree(part->data); + curlx_safefree(part->data); } /* Subparts callbacks. */ @@ -686,11 +690,16 @@ } /* Read a non-encoded part content. */ -static size_t read_part_content(curl_mimepart *part, - char *buffer, size_t bufsize, bool *hasread) + +static size_t read_part_content(curl_mimepart *part, char *buffer, + size_t bufsize, bool *hasread, + size_t call_depth) { size_t sz = 0; + if(++call_depth > MAX_MIME_LEVELS) + return READ_ERROR; + switch(part->lastreadstatus) { case 0: case CURL_READFUNC_ABORT: @@ -713,7 +722,8 @@ * Cannot be processed as other kinds since read function requires * an additional parameter and is highly recursive. */ - sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread); + sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread, + call_depth); break; case MIMEKIND_FILE: if(part->fp && feof(part->fp)) @@ -752,13 +762,17 @@ /* Read and encode part content. */ static size_t read_encoded_part_content(curl_mimepart *part, char *buffer, - size_t bufsize, bool *hasread) + size_t bufsize, bool *hasread, + size_t call_depth) { struct mime_encoder_state *st = &part->encstate; size_t cursize = 0; size_t sz; bool ateof = FALSE; + if(++call_depth > MAX_MIME_LEVELS) + return READ_ERROR; + for(;;) { if(st->bufbeg < st->bufend || ateof) { /* Encode buffered data. */ @@ -791,7 +805,7 @@ if(st->bufend >= sizeof(st->buf)) return cursize ? cursize : READ_ERROR; /* Buffer full. */ sz = read_part_content(part, st->buf + st->bufend, - sizeof(st->buf) - st->bufend, hasread); + sizeof(st->buf) - st->bufend, hasread, call_depth); switch(sz) { case 0: ateof = TRUE; @@ -812,12 +826,15 @@ /* Readback a mime part. */ static size_t readback_part(curl_mimepart *part, - char *buffer, size_t bufsize, bool *hasread) + char *buffer, size_t bufsize, bool *hasread, + size_t call_depth) { size_t cursize = 0; - /* Readback from part. */ + if(++call_depth > MAX_MIME_LEVELS) + return READ_ERROR; + /* Readback from part. */ while(bufsize) { size_t sz = 0; struct curl_slist *hdr = (struct curl_slist *)part->state.ptr; @@ -860,9 +877,10 @@ break; case MIMESTATE_CONTENT: if(part->encoder) - sz = read_encoded_part_content(part, buffer, bufsize, hasread); + sz = read_encoded_part_content(part, buffer, bufsize, hasread, + call_depth); else - sz = read_part_content(part, buffer, bufsize, hasread); + sz = read_part_content(part, buffer, bufsize, hasread, call_depth); switch(sz) { case 0: mimesetstate(&part->state, MIMESTATE_END, NULL); @@ -896,12 +914,16 @@ /* Readback from mime. Warning: not a read callback function. */ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, - void *instream, bool *hasread) + void *instream, bool *hasread, + size_t call_depth) { curl_mime *mime = (curl_mime *)instream; size_t cursize = 0; (void)size; /* Always 1 */ + if(++call_depth > MAX_MIME_LEVELS) + return READ_ERROR; + while(nitems) { size_t sz = 0; curl_mimepart *part = mime->state.ptr; @@ -936,7 +958,7 @@ mimesetstate(&mime->state, MIMESTATE_END, NULL); break; } - sz = readback_part(part, buffer, nitems, hasread); + sz = readback_part(part, buffer, nitems, hasread, call_depth); switch(sz) { case CURL_READFUNC_ABORT: case CURL_READFUNC_PAUSE: @@ -1001,7 +1023,7 @@ { curl_mime *mime = (curl_mime *)instream; curl_mimepart *part; - int result = CURL_SEEKFUNC_OK; + int rc = CURL_SEEKFUNC_OK; if(whence != SEEK_SET || offset) return CURL_SEEKFUNC_CANTSEEK; /* Only support full rewind. */ @@ -1012,13 +1034,13 @@ for(part = mime->firstpart; part; part = part->nextpart) { int res = mime_part_rewind(part); if(res != CURL_SEEKFUNC_OK) - result = res; + rc = res; } - if(result == CURL_SEEKFUNC_OK) + if(rc == CURL_SEEKFUNC_OK) mimesetstate(&mime->state, MIMESTATE_BEGIN, NULL); - return result; + return rc; } /* Release part content. */ @@ -1071,9 +1093,9 @@ curl_slist_free_all(part->curlheaders); if(part->flags & MIME_USERHEADERS_OWNER) curl_slist_free_all(part->userheaders); - Curl_safefree(part->mimetype); - Curl_safefree(part->name); - Curl_safefree(part->filename); + curlx_safefree(part->mimetype); + curlx_safefree(part->name); + curlx_safefree(part->filename); Curl_mime_initpart(part); } } @@ -1241,7 +1263,7 @@ if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->name); + curlx_safefree(part->name); if(name) { part->name = curlx_strdup(name); @@ -1258,7 +1280,7 @@ if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->filename); + curlx_safefree(part->filename); if(filename) { part->filename = curlx_strdup(filename); @@ -1350,7 +1372,7 @@ if(!part) return CURLE_BAD_FUNCTION_ARGUMENT; - Curl_safefree(part->mimetype); + curlx_safefree(part->mimetype); if(mimetype) { part->mimetype = curlx_strdup(mimetype); @@ -1505,7 +1527,7 @@ * adding any data and this loops infinitely. */ do { hasread = FALSE; - ret = readback_part(part, buffer, nitems, &hasread); + ret = readback_part(part, buffer, nitems, &hasread, 0); /* * If this is not possible to get some data without calling more than * one read callback (probably because a content encoder is not able to @@ -1680,7 +1702,7 @@ const char *boundary = NULL; char *customct; const char *cte = NULL; - CURLcode ret = CURLE_OK; + CURLcode result = CURLE_OK; /* Get rid of previously prepared headers. */ curl_slist_free_all(part->curlheaders); @@ -1742,35 +1764,35 @@ if(part->name) { name = escape_string(data, part->name, strategy); if(!name) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } - if(!ret && part->filename) { + if(!result && part->filename) { filename = escape_string(data, part->filename, strategy); if(!filename) - ret = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } - if(!ret) - ret = Curl_mime_add_header(&part->curlheaders, - "Content-Disposition: %s%s%s%s%s%s%s", - disposition, - name ? "; name=\"" : "", - name ? name : "", - name ? "\"" : "", - filename ? "; filename=\"" : "", - filename ? filename : "", - filename ? "\"" : ""); - Curl_safefree(name); - Curl_safefree(filename); - if(ret) - return ret; + if(!result) + result = Curl_mime_add_header(&part->curlheaders, + "Content-Disposition: %s%s%s%s%s%s%s", + disposition, + name ? "; name=\"" : "", + name ? name : "", + name ? "\"" : "", + filename ? "; filename=\"" : "", + filename ? filename : "", + filename ? "\"" : ""); + curlx_safefree(name); + curlx_safefree(filename); + if(result) + return result; } } /* Issue Content-Type header. */ if(contenttype) { - ret = add_content_type(&part->curlheaders, contenttype, boundary); - if(ret) - return ret; + result = add_content_type(&part->curlheaders, contenttype, boundary); + if(result) + return result; } /* Content-Transfer-Encoding header. */ @@ -1782,10 +1804,10 @@ part->kind != MIMEKIND_MULTIPART) cte = "8bit"; if(cte) { - ret = Curl_mime_add_header(&part->curlheaders, - "Content-Transfer-Encoding: %s", cte); - if(ret) - return ret; + result = Curl_mime_add_header(&part->curlheaders, + "Content-Transfer-Encoding: %s", cte); + if(result) + return result; } } @@ -1802,13 +1824,13 @@ if(content_type_match(contenttype, STRCONST("multipart/form-data"))) disposition = "form-data"; for(subpart = mime->firstpart; subpart; subpart = subpart->nextpart) { - ret = Curl_mime_prepare_headers(data, subpart, NULL, - disposition, strategy); - if(ret) - return ret; + result = Curl_mime_prepare_headers(data, subpart, NULL, + disposition, strategy); + if(result) + return result; } } - return ret; + return result; } /* Recursively reset paused status in the given part. */ @@ -1929,14 +1951,14 @@ else nread = Curl_mime_read(buf, 1, blen, ctx->part); - CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zd", + CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zu", blen, nread); switch(nread) { case 0: if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { failf(data, "client mime read EOF fail, " - "only %"FMT_OFF_T"/%"FMT_OFF_T + "only %" FMT_OFF_T "/%" FMT_OFF_T " of needed bytes read", ctx->read_len, ctx->total_len); return CURLE_READ_ERROR; } @@ -1991,7 +2013,7 @@ } CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T - ", read=%"FMT_OFF_T") -> %d, %zu, %d", + ", read=%" FMT_OFF_T ") -> %d, %zu, %d", blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); return result; }
diff --git a/Utilities/cmcurl/lib/mprintf.c b/Utilities/cmcurl/lib/mprintf.c index c6a4a49..6eaea66 100644 --- a/Utilities/cmcurl/lib/mprintf.c +++ b/Utilities/cmcurl/lib/mprintf.c
@@ -39,11 +39,11 @@ /* Upper-case digits. */ const unsigned char Curl_udigits[] = "0123456789ABCDEF"; -#define OUTCHAR(x) \ - do { \ - if(stream((unsigned char)(x), userp)) \ - return TRUE; \ - (*donep)++; \ +#define OUTCHAR(x) \ + do { \ + if(stream((unsigned char)(x), userp)) \ + return TRUE; \ + (*donep)++; \ } while(0) /* Data type to read from the arglist */ @@ -169,6 +169,261 @@ #define PFMT_PRECARG 10 /* attempted to use same arg twice, for prec */ #define PFMT_MANYSEGS 11 /* maxed out output segments */ +static int parse_flags(const char **fmtp, unsigned int *flagsp, int use_dollar, + int *precp, int *widthp) +{ + const char *fmt = *fmtp; + bool loopit = TRUE; + unsigned int flags = 0; + int width = 0; + int precision = 0; + + /* Handle the flags */ + do { + switch(*fmt++) { + case ' ': + flags |= FLAGS_SPACE; + break; + case '+': + flags |= FLAGS_SHOWSIGN; + break; + case '-': + flags |= FLAGS_LEFT; + flags &= ~(unsigned int)FLAGS_PAD_NIL; + break; + case '#': + flags |= FLAGS_ALT; + break; + case '.': + if('*' == *fmt) { + /* The precision is picked from a specified parameter */ + flags |= FLAGS_PRECPARAM; + fmt++; + + if(use_dollar == DOLLAR_USE) { + precision = dollarstring(fmt, &fmt); + if(precision < 0) + /* illegal combo */ + return PFMT_DOLLARPREC; + } + else + /* get it from the next argument */ + precision = -1; + } + else { + bool is_neg; + curl_off_t num; + flags |= FLAGS_PREC; + is_neg = ('-' == *fmt); + if(is_neg) + fmt++; + if(curlx_str_number(&fmt, &num, INT_MAX)) + return PFMT_PREC; + precision = (int)num; + if(is_neg) + precision = -precision; + } + if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) == + (FLAGS_PREC | FLAGS_PRECPARAM)) + /* it is not permitted to use both kinds of precision for the same + argument */ + return PFMT_PRECMIX; + break; + case 'h': + flags |= FLAGS_SHORT; + break; +#ifdef _WIN32 + case 'I': + /* Non-ANSI integer extensions I32 I64 */ + if((fmt[0] == '3') && (fmt[1] == '2')) { + flags |= FLAGS_LONG; + fmt += 2; + } + else if((fmt[0] == '6') && (fmt[1] == '4')) { + flags |= FLAGS_LONGLONG; + fmt += 2; + } + else { +#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + } + break; +#endif /* _WIN32 */ + case 'l': + if(flags & FLAGS_LONG) + flags |= FLAGS_LONGLONG; + else + flags |= FLAGS_LONG; + break; + case 'L': + flags |= FLAGS_LONGDOUBLE; + break; + case 'q': + flags |= FLAGS_LONGLONG; + break; + case 'z': + /* the code below generates a warning if -Wunreachable-code is + used */ +#if (SIZEOF_SIZE_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + break; + case 'O': +#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) + flags |= FLAGS_LONGLONG; +#else + flags |= FLAGS_LONG; +#endif + break; + case '0': + if(!(flags & FLAGS_LEFT)) + flags |= FLAGS_PAD_NIL; + FALLTHROUGH(); + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + curl_off_t num; + flags |= FLAGS_WIDTH; + fmt--; + if(curlx_str_number(&fmt, &num, INT_MAX)) + return PFMT_WIDTH; + width = (int)num; + break; + } + case '*': /* read width from argument list */ + flags |= FLAGS_WIDTHPARAM; + if(use_dollar == DOLLAR_USE) { + width = dollarstring(fmt, &fmt); + if(width < 0) + /* illegal combo */ + return PFMT_DOLLARWIDTH; + } + else + /* pick from the next argument */ + width = -1; + break; + default: + loopit = FALSE; + fmt--; + break; + } /* switch */ + } while(loopit); /* do */ + *flagsp = flags; + *precp = precision; + *widthp = width; + *fmtp = fmt; + return PFMT_OK; +} + +static bool parse_conversion(const char f, unsigned int *flagp, + FormatType *typep) +{ + unsigned int flags = *flagp; + FormatType type; + switch(f) { + case 'S': + flags |= FLAGS_ALT; + type = MTYPE_STRING; + break; + case 's': + type = MTYPE_STRING; + break; + case 'n': + type = MTYPE_INTPTR; + break; + case 'p': + type = MTYPE_PTR; + break; + case 'd': + case 'i': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONG; + else if(flags & FLAGS_LONG) + type = MTYPE_LONG; + else + type = MTYPE_INT; + break; + case 'u': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONGU; + else if(flags & FLAGS_LONG) + type = MTYPE_LONGU; + else + type = MTYPE_INTU; + flags |= FLAGS_UNSIGNED; + break; + case 'o': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONGU; + else if(flags & FLAGS_LONG) + type = MTYPE_LONGU; + else + type = MTYPE_INTU; + flags |= FLAGS_OCTAL | FLAGS_UNSIGNED; + break; + case 'x': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONGU; + else if(flags & FLAGS_LONG) + type = MTYPE_LONGU; + else + type = MTYPE_INTU; + flags |= FLAGS_HEX | FLAGS_UNSIGNED; + break; + case 'X': + if(flags & FLAGS_LONGLONG) + type = MTYPE_LONGLONGU; + else if(flags & FLAGS_LONG) + type = MTYPE_LONGU; + else + type = MTYPE_INTU; + flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED; + break; + case 'c': + type = MTYPE_INT; + flags |= FLAGS_CHAR; + break; + case 'f': + type = MTYPE_DOUBLE; + break; + case 'e': + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATE; + break; + case 'E': + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATE | FLAGS_UPPER; + break; + case 'g': + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATG; + break; + case 'G': + type = MTYPE_DOUBLE; + flags |= FLAGS_FLOATG | FLAGS_UPPER; + break; + default: + /* invalid instruction, disregard and continue */ + return TRUE; + } /* switch */ + + *flagp |= flags; + *typep = type; + return FALSE; +} + + static int parsefmt(const char *format, struct outsegment *out, struct va_input *in, @@ -192,12 +447,12 @@ while(*fmt) { if(*fmt == '%') { struct va_input *iptr; - bool loopit = TRUE; FormatType type; unsigned int flags = 0; int width = 0; int precision = 0; int param = -1; + int rc; fmt++; outlen = (size_t)(fmt - start - 1); if(*fmt == '%') { @@ -232,233 +487,12 @@ use_dollar = DOLLAR_USE; } - /* Handle the flags */ - while(loopit) { - switch(*fmt++) { - case ' ': - flags |= FLAGS_SPACE; - break; - case '+': - flags |= FLAGS_SHOWSIGN; - break; - case '-': - flags |= FLAGS_LEFT; - flags &= ~(unsigned int)FLAGS_PAD_NIL; - break; - case '#': - flags |= FLAGS_ALT; - break; - case '.': - if('*' == *fmt) { - /* The precision is picked from a specified parameter */ - flags |= FLAGS_PRECPARAM; - fmt++; + rc = parse_flags(&fmt, &flags, use_dollar, &precision, &width); + if(rc) + return rc; - if(use_dollar == DOLLAR_USE) { - precision = dollarstring(fmt, &fmt); - if(precision < 0) - /* illegal combo */ - return PFMT_DOLLARPREC; - } - else - /* get it from the next argument */ - precision = -1; - } - else { - bool is_neg; - curl_off_t num; - flags |= FLAGS_PREC; - is_neg = ('-' == *fmt); - if(is_neg) - fmt++; - if(curlx_str_number(&fmt, &num, INT_MAX)) - return PFMT_PREC; - precision = (int)num; - if(is_neg) - precision = -precision; - } - if((flags & (FLAGS_PREC | FLAGS_PRECPARAM)) == - (FLAGS_PREC | FLAGS_PRECPARAM)) - /* it is not permitted to use both kinds of precision for the same - argument */ - return PFMT_PRECMIX; - break; - case 'h': - flags |= FLAGS_SHORT; - break; -#ifdef _WIN32 - case 'I': - /* Non-ANSI integer extensions I32 I64 */ - if((fmt[0] == '3') && (fmt[1] == '2')) { - flags |= FLAGS_LONG; - fmt += 2; - } - else if((fmt[0] == '6') && (fmt[1] == '4')) { - flags |= FLAGS_LONGLONG; - fmt += 2; - } - else { -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - } - break; -#endif /* _WIN32 */ - case 'l': - if(flags & FLAGS_LONG) - flags |= FLAGS_LONGLONG; - else - flags |= FLAGS_LONG; - break; - case 'L': - flags |= FLAGS_LONGDOUBLE; - break; - case 'q': - flags |= FLAGS_LONGLONG; - break; - case 'z': - /* the code below generates a warning if -Wunreachable-code is - used */ -#if (SIZEOF_SIZE_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - break; - case 'O': -#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG) - flags |= FLAGS_LONGLONG; -#else - flags |= FLAGS_LONG; -#endif - break; - case '0': - if(!(flags & FLAGS_LEFT)) - flags |= FLAGS_PAD_NIL; - FALLTHROUGH(); - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - curl_off_t num; - flags |= FLAGS_WIDTH; - fmt--; - if(curlx_str_number(&fmt, &num, INT_MAX)) - return PFMT_WIDTH; - width = (int)num; - break; - } - case '*': /* read width from argument list */ - flags |= FLAGS_WIDTHPARAM; - if(use_dollar == DOLLAR_USE) { - width = dollarstring(fmt, &fmt); - if(width < 0) - /* illegal combo */ - return PFMT_DOLLARWIDTH; - } - else - /* pick from the next argument */ - width = -1; - break; - default: - loopit = FALSE; - fmt--; - break; - } /* switch */ - } /* while */ - - switch(*fmt) { - case 'S': - flags |= FLAGS_ALT; - FALLTHROUGH(); - case 's': - type = MTYPE_STRING; - break; - case 'n': - type = MTYPE_INTPTR; - break; - case 'p': - type = MTYPE_PTR; - break; - case 'd': - case 'i': - if(flags & FLAGS_LONGLONG) - type = MTYPE_LONGLONG; - else if(flags & FLAGS_LONG) - type = MTYPE_LONG; - else - type = MTYPE_INT; - break; - case 'u': - if(flags & FLAGS_LONGLONG) - type = MTYPE_LONGLONGU; - else if(flags & FLAGS_LONG) - type = MTYPE_LONGU; - else - type = MTYPE_INTU; - flags |= FLAGS_UNSIGNED; - break; - case 'o': - if(flags & FLAGS_LONGLONG) - type = MTYPE_LONGLONGU; - else if(flags & FLAGS_LONG) - type = MTYPE_LONGU; - else - type = MTYPE_INTU; - flags |= FLAGS_OCTAL | FLAGS_UNSIGNED; - break; - case 'x': - if(flags & FLAGS_LONGLONG) - type = MTYPE_LONGLONGU; - else if(flags & FLAGS_LONG) - type = MTYPE_LONGU; - else - type = MTYPE_INTU; - flags |= FLAGS_HEX | FLAGS_UNSIGNED; - break; - case 'X': - if(flags & FLAGS_LONGLONG) - type = MTYPE_LONGLONGU; - else if(flags & FLAGS_LONG) - type = MTYPE_LONGU; - else - type = MTYPE_INTU; - flags |= FLAGS_HEX | FLAGS_UPPER | FLAGS_UNSIGNED; - break; - case 'c': - type = MTYPE_INT; - flags |= FLAGS_CHAR; - break; - case 'f': - type = MTYPE_DOUBLE; - break; - case 'e': - type = MTYPE_DOUBLE; - flags |= FLAGS_FLOATE; - break; - case 'E': - type = MTYPE_DOUBLE; - flags |= FLAGS_FLOATE | FLAGS_UPPER; - break; - case 'g': - type = MTYPE_DOUBLE; - flags |= FLAGS_FLOATG; - break; - case 'G': - type = MTYPE_DOUBLE; - flags |= FLAGS_FLOATG | FLAGS_UPPER; - break; - default: - /* invalid instruction, disregard and continue */ + if(parse_conversion(*fmt, &flags, &type)) continue; - } /* switch */ if(flags & FLAGS_WIDTHPARAM) { if(width < 0) @@ -672,22 +706,19 @@ /* NOTE NOTE NOTE!! Not all sprintf implementations return number of output characters */ -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #endif #ifdef _WIN32 curlx_win32_snprintf(work, BUFFSIZE, fmt, dnum); -#elif defined(HAVE_SNPRINTF) +#else /* !checksrc! disable BANNEDFUNC 1 */ /* !checksrc! disable LONGLINE */ /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */ snprintf(work, BUFFSIZE, fmt, dnum); -#else - /* float and double outputs do not work without snprintf support */ - work[0] = 0; #endif -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif DEBUGASSERT(strlen(work) < BUFFSIZE); @@ -1074,7 +1105,7 @@ } int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format, - va_list ap_save) + va_list args) { int retcode; struct nsprintf info; @@ -1083,7 +1114,7 @@ info.length = 0; info.max = maxlength; - retcode = formatf(&info, addbyter, format, ap_save); + retcode = formatf(&info, addbyter, format, args); if(info.max) { /* we terminate this with a zero byte */ if(info.max == info.length) { @@ -1101,10 +1132,10 @@ int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = curl_mvsnprintf(buffer, maxlength, format, args); + va_end(args); return retcode; } @@ -1121,13 +1152,13 @@ } /* appends the formatted string, returns MERR error code */ -int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) +int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list args) { struct asprintf info; info.b = dyn; info.merr = MERR_OK; - (void)formatf(&info, alloc_addbyter, format, ap_save); + (void)formatf(&info, alloc_addbyter, format, args); if(info.merr) { curlx_dyn_free(info.b); return info.merr; @@ -1135,7 +1166,7 @@ return 0; } -char *curl_mvaprintf(const char *format, va_list ap_save) +char *curl_mvaprintf(const char *format, va_list args) { struct asprintf info; struct dynbuf dyn; @@ -1143,7 +1174,7 @@ curlx_dyn_init(info.b, DYN_APRINTF); info.merr = MERR_OK; - (void)formatf(&info, alloc_addbyter, format, ap_save); + (void)formatf(&info, alloc_addbyter, format, args); if(info.merr) { curlx_dyn_free(info.b); return NULL; @@ -1155,11 +1186,11 @@ char *curl_maprintf(const char *format, ...) { - va_list ap_save; + va_list args; char *s; - va_start(ap_save, format); - s = curl_mvaprintf(format, ap_save); - va_end(ap_save); + va_start(args, format); + s = curl_mvaprintf(format, args); + va_end(args); return s; } @@ -1173,11 +1204,11 @@ int curl_msprintf(char *buffer, const char *format, ...) { - va_list ap_save; /* argument pointer */ + va_list args; /* argument pointer */ int retcode; - va_start(ap_save, format); - retcode = formatf(&buffer, storebuffer, format, ap_save); - va_end(ap_save); + va_start(args, format); + retcode = formatf(&buffer, storebuffer, format, args); + va_end(args); *buffer = 0; /* we terminate this with a zero byte */ return retcode; } @@ -1193,36 +1224,36 @@ int curl_mprintf(const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = formatf(stdout, fputc_wrapper, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = formatf(stdout, fputc_wrapper, format, args); + va_end(args); return retcode; } -int curl_mfprintf(FILE *whereto, const char *format, ...) +int curl_mfprintf(FILE *fd, const char *format, ...) { int retcode; - va_list ap_save; /* argument pointer */ - va_start(ap_save, format); - retcode = formatf(whereto, fputc_wrapper, format, ap_save); - va_end(ap_save); + va_list args; /* argument pointer */ + va_start(args, format); + retcode = formatf(fd, fputc_wrapper, format, args); + va_end(args); return retcode; } -int curl_mvsprintf(char *buffer, const char *format, va_list ap_save) +int curl_mvsprintf(char *buffer, const char *format, va_list args) { - int retcode = formatf(&buffer, storebuffer, format, ap_save); + int retcode = formatf(&buffer, storebuffer, format, args); *buffer = 0; /* we terminate this with a zero byte */ return retcode; } -int curl_mvprintf(const char *format, va_list ap_save) +int curl_mvprintf(const char *format, va_list args) { - return formatf(stdout, fputc_wrapper, format, ap_save); + return formatf(stdout, fputc_wrapper, format, args); } -int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save) +int curl_mvfprintf(FILE *fd, const char *format, va_list args) { - return formatf(whereto, fputc_wrapper, format, ap_save); + return formatf(fd, fputc_wrapper, format, args); }
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c index e591cfc..84fd272 100644 --- a/Utilities/cmcurl/lib/mqtt.c +++ b/Utilities/cmcurl/lib/mqtt.c
@@ -351,8 +351,8 @@ end: if(packet) curlx_free(packet); - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); return result; } @@ -964,7 +964,7 @@ /* * MQTTS protocol. */ -static const struct Curl_protocol Curl_protocol_mqtts = { +const struct Curl_protocol Curl_protocol_mqtts = { mqtt_setup_conn, /* setup_connection */ mqtt_do, /* do_it */ mqtt_done, /* done */ @@ -979,7 +979,7 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; @@ -989,7 +989,7 @@ /* * MQTT protocol. */ -static const struct Curl_protocol Curl_protocol_mqtt = { +const struct Curl_protocol Curl_protocol_mqtt = { mqtt_setup_conn, /* setup_connection */ mqtt_do, /* do_it */ mqtt_done, /* done */ @@ -1004,38 +1004,9 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif /* CURL_DISABLE_MQTT */ - -const struct Curl_scheme Curl_scheme_mqtts = { - "mqtts", /* scheme */ -#if defined(CURL_DISABLE_MQTT) || !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_mqtts, -#endif - CURLPROTO_MQTTS, /* protocol */ - CURLPROTO_MQTT, /* family */ - PROTOPT_SSL, /* flags */ - PORT_MQTTS, /* defport */ -}; - -/* - * MQTT protocol. - */ -const struct Curl_scheme Curl_scheme_mqtt = { - "mqtt", /* scheme */ -#ifdef CURL_DISABLE_MQTT - ZERO_NULL, -#else - &Curl_protocol_mqtt, -#endif - CURLPROTO_MQTT, /* protocol */ - CURLPROTO_MQTT, /* family */ - PROTOPT_NONE, /* flags */ - PORT_MQTT, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h index 67d1df4..3d95293 100644 --- a/Utilities/cmcurl/lib/mqtt.h +++ b/Utilities/cmcurl/lib/mqtt.h
@@ -23,7 +23,11 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_mqtt; -extern const struct Curl_scheme Curl_scheme_mqtts; +#ifndef CURL_DISABLE_MQTT +extern const struct Curl_protocol Curl_protocol_mqtt; +#ifdef USE_SSL +extern const struct Curl_protocol Curl_protocol_mqtts; +#endif +#endif #endif /* HEADER_CURL_MQTT_H */
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c index 6133736..7520253 100644 --- a/Utilities/cmcurl/lib/multi.c +++ b/Utilities/cmcurl/lib/multi.c
@@ -74,7 +74,6 @@ #define CURL_MULTI_HANDLE 0x000bab1e - #ifdef DEBUGBUILD /* On a debug build, we want to fail hard on multi handles that * are not NULL, but no longer have the MAGIC touch. This gives @@ -140,7 +139,6 @@ NULL, /* PENDING */ NULL, /* SETUP */ Curl_init_CONNECT, /* CONNECT */ - NULL, /* RESOLVING */ NULL, /* CONNECTING */ NULL, /* PROTOCONNECT */ NULL, /* PROTOCONNECTING */ @@ -207,7 +205,7 @@ static void ph_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -254,6 +252,10 @@ multi->multiplexing = TRUE; multi->max_concurrent_streams = 100; multi->last_timeout_ms = -1; +#ifdef ENABLE_WAKEUP + multi->wakeup_pair[0] = CURL_SOCKET_BAD; + multi->wakeup_pair[1] = CURL_SOCKET_BAD; +#endif if(Curl_mntfy_resize(multi) || Curl_uint32_bset_resize(&multi->process, xfer_table_size) || @@ -294,22 +296,35 @@ multi->wsa_event = WSACreateEvent(); if(multi->wsa_event == WSA_INVALID_EVENT) goto error; -#elif defined(ENABLE_WAKEUP) - if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) { - multi->wakeup_pair[0] = CURL_SOCKET_BAD; - multi->wakeup_pair[1] = CURL_SOCKET_BAD; - } +#endif +#ifdef ENABLE_WAKEUP + /* When enabled, rely on this to work. We ignore this in previous + * versions, but that seems an unnecessary complication. */ + if(Curl_wakeup_init(multi->wakeup_pair, TRUE) < 0) + goto error; #endif -#ifdef USE_IPV6 if(Curl_probeipv6(multi)) goto error; + +#ifdef USE_RESOLV_THREADED + if(xfer_table_size < CURL_XFER_TABLE_SIZE) { /* easy multi */ + if(Curl_async_thrdd_multi_init(multi, 0, 2, 10)) + goto error; + } + else { /* real multi handle */ + if(Curl_async_thrdd_multi_init(multi, 0, 20, 2000)) + goto error; + } #endif return multi; error: +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_destroy(multi, TRUE); +#endif Curl_multi_ev_cleanup(multi); Curl_hash_destroy(&multi->proto_hash); Curl_dnscache_destroy(&multi->dnscache); @@ -319,6 +334,7 @@ Curl_ssl_scache_destroy(multi->ssl_scache); #endif if(multi->admin) { + Curl_multi_ev_xfer_done(multi, multi->admin); multi->admin->multi = NULL; Curl_close(&multi->admin); } @@ -329,6 +345,9 @@ Curl_uint32_bset_destroy(&multi->pending); Curl_uint32_bset_destroy(&multi->msgsent); Curl_uint32_tbl_destroy(&multi->xfers); +#ifdef ENABLE_WAKEUP + Curl_wakeup_destroy(multi->wakeup_pair); +#endif curlx_free(multi); return NULL; @@ -362,6 +381,17 @@ return data->mstate < MSTATE_DO; } +static CURLMcode multi_assess_wakeup(struct Curl_multi *multi) +{ +#ifdef ENABLE_WAKEUP + if(multi->socket_cb) + return Curl_multi_ev_assess_xfer(multi, multi->admin); +#else + (void)multi; +#endif + return CURLM_OK; +} + static CURLMcode multi_xfers_add(struct Curl_multi *multi, struct Curl_easy *data) { @@ -421,11 +451,11 @@ return CURLM_OK; } -CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) +CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) { CURLMcode mresult; struct Curl_multi *multi = m; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; /* First, make some basic checks that the CURLM handle is a good handle */ if(!GOOD_MULTI_HANDLE(multi)) @@ -528,6 +558,12 @@ data->set.server_response_timeout; multi->admin->set.no_signal = data->set.no_signal; + mresult = multi_assess_wakeup(multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; + } + CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u", data->mid, Curl_multi_xfers_running(multi), Curl_uint32_tbl_count(&multi->xfers)); @@ -600,22 +636,6 @@ void *userdata) { struct multi_done_ctx *mdctx = userdata; -#ifdef CURLVERBOSE - const char *host = -#ifndef CURL_DISABLE_PROXY - conn->bits.socksproxy ? - conn->socks_proxy.host.dispname : - conn->bits.httpproxy ? conn->http_proxy.host.dispname : -#endif - conn->bits.conn_to_host ? conn->conn_to_host.dispname : - conn->host.dispname; - int port = -#ifndef CURL_DISABLE_PROXY - conn->bits.httpproxy ? conn->http_proxy.port : -#endif - conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; -#endif /* CURLVERBOSE */ Curl_detach_connection(data); @@ -630,22 +650,21 @@ data->state.done = TRUE; /* called now! */ data->state.recent_conn_id = conn->connection_id; - Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */ - Curl_resolv_unlink(data, &data->state.dns[1]); Curl_dnscache_prune(data); if(multi_conn_should_close(conn, data, (bool)mdctx->premature)) { - CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s:%d, " + CURL_TRC_M(data, "multi_done, terminating conn #%" FMT_OFF_T " to %s, " "forbid=%d, close=%d, premature=%d, conn_multiplex=%d", - conn->connection_id, host, port, data->set.reuse_forbid, - conn->bits.close, mdctx->premature, + conn->connection_id, conn->destination, + data->set.reuse_forbid, conn->bits.close, mdctx->premature, Curl_conn_is_multiplex(conn, FIRSTSOCKET)); connclose(conn, "disconnecting"); Curl_conn_terminate(data, conn, (bool)mdctx->premature); } else if(!Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) { - CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s:%d was shutdown" - " by server, not reusing", conn->connection_id, host, port); + CURL_TRC_M(data, "multi_done, conn #%" FMT_OFF_T " to %s was shutdown" + " by server, not reusing", conn->connection_id, + conn->destination); connclose(conn, "server shutdown"); Curl_conn_terminate(data, conn, (bool)mdctx->premature); } @@ -654,8 +673,8 @@ if(Curl_cpool_conn_now_idle(data, conn)) { /* connection kept in the cpool */ data->state.lastconnect_id = conn->connection_id; - infof(data, "Connection #%" FMT_OFF_T " to host %s:%d left intact", - conn->connection_id, host, port); + infof(data, "Connection #%" FMT_OFF_T " to host %s left intact", + conn->connection_id, conn->destination); } else { /* connection was removed from the cpool and destroyed. */ @@ -671,9 +690,6 @@ { CURLcode result; struct connectdata *conn = data->conn; - struct multi_done_ctx mdctx; - - memset(&mdctx, 0, sizeof(mdctx)); CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d", (int)status, (int)premature, data->state.done); @@ -683,11 +699,11 @@ return CURLE_OK; /* Shut down any ongoing async resolver operation. */ - Curl_async_shutdown(data); + Curl_resolv_shutdown_all(data); /* Cleanup possible redirect junk */ - Curl_safefree(data->req.newurl); - Curl_safefree(data->req.location); + curlx_safefree(data->req.newurl); + curlx_safefree(data->req.location); switch(status) { case CURLE_ABORTED_BY_CALLBACK: @@ -704,34 +720,43 @@ } /* this calls the protocol-specific function pointer previously set */ - if(conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT)) + if(conn && conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT)) result = conn->scheme->run->done(data, status, premature); else result = status; - if(result != CURLE_ABORTED_BY_CALLBACK) { - /* avoid this if we already aborted by callback to avoid this calling - another callback */ + if(data->mstate > MSTATE_CONNECTING && + (result != CURLE_ABORTED_BY_CALLBACK)) { + /* avoid this if + * - the transfer has not connected + * - we already aborted by callback to avoid this calling another callback + */ int rc = Curl_pgrsDone(data); if(!result && rc) result = CURLE_ABORTED_BY_CALLBACK; } /* Make sure that transfer client writes are really done now. */ - result = Curl_1st_err(result, Curl_xfer_write_done(data, premature)); + result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature)); /* Inform connection filters that this transfer is done */ - Curl_conn_ev_data_done(data, premature); + if(conn) + Curl_conn_ev_data_done(data, premature); process_pending_handles(data->multi); /* connection / multiplex */ if(!result) result = Curl_req_done(&data->req, data, premature); - /* Under the potential connection pool's share lock, decide what to - * do with the transfer's connection. */ - mdctx.premature = premature; - Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); + if(conn) { + /* Under the potential connection pool's share lock, decide what to + * do with the transfer's connection. */ + struct multi_done_ctx mdctx; + + memset(&mdctx, 0, sizeof(mdctx)); + mdctx.premature = premature; + Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx); + } /* flush the netrc cache */ Curl_netrc_cleanup(&data->state.netrc); @@ -744,14 +769,14 @@ { (void)userdata; (void)data; - if(conn->connect_only) + if(conn->bits.connect_only) connclose(conn, "Removing connect-only easy handle"); } -CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) +CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) { struct Curl_multi *multi = m; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; bool premature; struct Curl_llist_node *e; CURLMcode mresult; @@ -886,6 +911,12 @@ if(mresult) return mresult; + mresult = multi_assess_wakeup(multi); + if(mresult) { + failf(data, "error enabling wakeup listening: %d", mresult); + return mresult; + } + CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u", mid, Curl_multi_xfers_running(multi), Curl_uint32_tbl_count(&multi->xfers)); @@ -1080,7 +1111,7 @@ if(conn->scheme->run->perform_pollset) result = conn->scheme->run->perform_pollset(data, ps); else { - /* Default is to obey the data->req.keepon flags for send/recv */ + /* Default is to obey the request flags for send/recv */ if(Curl_req_want_recv(data) && CONN_SOCK_IDX_VALID(conn->recv_idx)) { result = Curl_pollset_add_in(data, ps, conn->sock[conn->recv_idx]); } @@ -1101,75 +1132,77 @@ CURLMcode Curl_multi_pollset(struct Curl_easy *data, struct easy_pollset *ps) { - CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; + Curl_pollset_reset(ps); +#ifdef ENABLE_WAKEUP + /* The admin handle always listens on the wakeup socket when there + * are transfers alive. */ + if(data->multi && (data == data->multi->admin) && + data->multi->xfers_alive) { + result = Curl_pollset_add_in(data, ps, data->multi->wakeup_pair[0]); + } +#endif /* If the transfer has no connection, this is fine. Happens when called via curl_multi_remove_handle() => Curl_multi_ev_assess() => Curl_multi_pollset(). */ - Curl_pollset_reset(ps); - if(!data->conn) - return CURLM_OK; + if(!result && data->conn) { + switch(data->mstate) { + case MSTATE_INIT: + case MSTATE_PENDING: + case MSTATE_SETUP: + case MSTATE_CONNECT: + /* nothing to poll for yet */ + break; - switch(data->mstate) { - case MSTATE_INIT: - case MSTATE_PENDING: - case MSTATE_SETUP: - case MSTATE_CONNECT: - /* nothing to poll for yet */ - break; + case MSTATE_CONNECTING: + if(data->conn && !data->conn->bits.dns_resolved) + result = Curl_resolv_pollset(data, ps); + if(!result) + result = mstate_connecting_pollset(data, ps); + break; - case MSTATE_RESOLVING: - result = Curl_resolv_pollset(data, ps); - break; + case MSTATE_PROTOCONNECT: + case MSTATE_PROTOCONNECTING: + result = mstate_protocol_pollset(data, ps); + break; - case MSTATE_CONNECTING: - result = mstate_connecting_pollset(data, ps); - break; + case MSTATE_DO: + case MSTATE_DOING: + result = mstate_do_pollset(data, ps); + break; - case MSTATE_PROTOCONNECT: - case MSTATE_PROTOCONNECTING: - result = mstate_protocol_pollset(data, ps); - break; + case MSTATE_DOING_MORE: + result = mstate_domore_pollset(data, ps); + break; - case MSTATE_DO: - case MSTATE_DOING: - result = mstate_do_pollset(data, ps); - break; + case MSTATE_DID: /* same as PERFORMING in regard to polling */ + case MSTATE_PERFORMING: + result = mstate_perform_pollset(data, ps); + break; - case MSTATE_DOING_MORE: - result = mstate_domore_pollset(data, ps); - break; + case MSTATE_RATELIMITING: + /* we need to let time pass, ignore socket(s) */ + break; - case MSTATE_DID: /* same as PERFORMING in regard to polling */ - case MSTATE_PERFORMING: - result = mstate_perform_pollset(data, ps); - break; + case MSTATE_DONE: + case MSTATE_COMPLETED: + case MSTATE_MSGSENT: + /* nothing more to poll for */ + break; - case MSTATE_RATELIMITING: - /* we need to let time pass, ignore socket(s) */ - break; - - case MSTATE_DONE: - case MSTATE_COMPLETED: - case MSTATE_MSGSENT: - /* nothing more to poll for */ - break; - - default: - failf(data, "multi_getsock: unexpected multi state %d", data->mstate); - DEBUGASSERT(0); - break; + default: + failf(data, "multi_getsock: unexpected multi state %d", data->mstate); + DEBUGASSERT(0); + break; + } } if(result) { if(result == CURLE_OUT_OF_MEMORY) - mresult = CURLM_OUT_OF_MEMORY; - else { - failf(data, "error determining pollset: %d", result); - mresult = CURLM_INTERNAL_ERROR; - } - goto out; + return CURLM_OUT_OF_MEMORY; + failf(data, "error determining pollset: %d", result); + return CURLM_INTERNAL_ERROR; } #ifdef CURLVERBOSE @@ -1208,8 +1241,7 @@ } #endif -out: - return mresult; + return CURLM_OK; } CURLMcode curl_multi_fdset(CURLM *m, @@ -1328,7 +1360,167 @@ if(!getsockopt(s, SOL_SOCKET, SO_TYPE, (char *)&t, &l) && t == SOCK_STREAM) swrite(s, NULL, 0); } -#endif + +static CURLMcode multi_winsock_select(struct Curl_multi *multi, + struct curl_pollfds *cpfds, + unsigned int curl_nfds, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + bool extrawait, + int *pnevents) +{ + CURLMcode mresult = CURLM_OK; + WSANETWORKEVENTS wsa_events; + int nevents = 0; + size_t i; + + DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); + + /* Set the WSA events based on the collected pollds */ + for(i = 0; i < cpfds->n; i++) { + long mask = 0; + if(cpfds->pfds[i].events & POLLIN) + mask |= FD_READ | FD_ACCEPT | FD_CLOSE; + if(cpfds->pfds[i].events & POLLPRI) + mask |= FD_OOB; + if(cpfds->pfds[i].events & POLLOUT) { + mask |= FD_WRITE | FD_CONNECT | FD_CLOSE; + reset_socket_fdwrite(cpfds->pfds[i].fd); + } + if(mask) { + if(WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, mask) != 0) { + mresult = CURLM_OUT_OF_MEMORY; + goto out; + } + } + } + + if(cpfds->n || extrawait) { + int pollrc = 0; + + if(cpfds->n) { /* pre-check with Winsock */ + pollrc = Curl_poll(cpfds->pfds, cpfds->n, 0); + if(pollrc < 0) { + mresult = CURLM_UNRECOVERABLE_POLL; + goto out; + } + nevents = pollrc; + } + + if(!nevents) { + /* now wait... if not ready during the pre-check (pollrc == 0) */ + WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, + FALSE); + } + + /* With Winsock, we have to run the following section unconditionally + to call WSAEventSelect(fd, event, 0) on all the sockets */ + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ + for(i = 0; i < extra_nfds; i++) { + unsigned short mask = 0; + curl_socket_t s = extra_fds[i].fd; + + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { + if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) + mask |= CURL_WAIT_POLLIN; + if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE)) + mask |= CURL_WAIT_POLLOUT; + if(wsa_events.lNetworkEvents & FD_OOB) + mask |= CURL_WAIT_POLLPRI; + if(!pollrc && wsa_events.lNetworkEvents) + nevents++; + } + WSAEventSelect(s, multi->wsa_event, 0); + if(!pollrc) { + extra_fds[i].revents = (short)mask; + continue; + } + else { + unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents; + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; + extra_fds[i].revents = (short)mask; + } + } + + /* Count up all our own sockets that had activity, + and remove them from the event. */ + for(i = 0; i < curl_nfds; ++i) { + wsa_events.lNetworkEvents = 0; + if(WSAEnumNetworkEvents(cpfds->pfds[i].fd, NULL, &wsa_events) == 0) { + if(!pollrc && wsa_events.lNetworkEvents) + nevents++; + } + WSAEventSelect(cpfds->pfds[i].fd, multi->wsa_event, 0); + } + WSAResetEvent(multi->wsa_event); + } + +out: + *pnevents = nevents; + return mresult; +} + +#else /* USE_WINSOCK */ + +static CURLMcode multi_posix_poll(struct Curl_multi *multi, + struct curl_pollfds *cpfds, + unsigned int curl_nfds, + struct curl_waitfd extra_fds[], + unsigned int extra_nfds, + int timeout_ms, + bool extrawait, + int *pnevents) +{ + CURLMcode mresult = CURLM_OK; + int nevents = 0; + size_t i; + + (void)multi; + if(cpfds->n) { + int pollrc = Curl_poll(cpfds->pfds, cpfds->n, timeout_ms); /* wait... */ + if(pollrc < 0) { + mresult = CURLM_UNRECOVERABLE_POLL; + goto out; + } + nevents = pollrc; + + /* copy revents results from the poll to the curl_multi_wait poll + struct, the bit values of the actual underlying poll() implementation + may not be the same as the ones in the public libcurl API! */ + for(i = 0; i < extra_nfds; i++) { + unsigned r = (unsigned)cpfds->pfds[curl_nfds + i].revents; + unsigned short mask = 0; + if(r & POLLIN) + mask |= CURL_WAIT_POLLIN; + if(r & POLLOUT) + mask |= CURL_WAIT_POLLOUT; + if(r & POLLPRI) + mask |= CURL_WAIT_POLLPRI; + extra_fds[i].revents = (short)mask; + } + } + else if(extrawait) { + /* No fds to poll, but asked to obey timeout_ms anyway. We cannot + * use Curl_poll() as it, on some platforms, returns immediately + * without fds. */ + curlx_wait_ms(timeout_ms); + } + +out: + *pnevents = nevents; + return mresult; +} + +#endif /* !USE_WINSOCK */ #define NUM_POLLS_ON_STACK 10 @@ -1337,13 +1529,12 @@ unsigned int extra_nfds, int timeout_ms, int *ret, - bool extrawait, /* when no socket, wait */ - bool use_wakeup) + bool extrawait) /* when no socket, wait */ { size_t i; struct curltime expire_time; long timeout_internal; - int retcode = 0; + int nevents = 0; struct easy_pollset ps; struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct curl_pollfds cpfds; @@ -1351,13 +1542,8 @@ struct Curl_easy *data = NULL; CURLMcode mresult = CURLM_OK; uint32_t mid; - -#ifdef USE_WINSOCK - WSANETWORKEVENTS wsa_events; - DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT); -#endif -#ifndef ENABLE_WAKEUP - (void)use_wakeup; +#ifdef ENABLE_WAKEUP + int wakeup_idx = -1; #endif if(!GOOD_MULTI_HANDLE(multi)) @@ -1395,6 +1581,20 @@ goto out; } +#ifdef ENABLE_WAKEUP + /* If `extrawait` is TRUE *or* we have `extra_fds`to poll *or* we + * have transfer sockets to poll, we obey `timeout_ms`. + * Then we need to also monitor the multi's wakeup + * socket to catch calls to `curl_multi_wakeup()` during the wait. */ + if(extrawait || cpfds.n || extra_nfds) { + wakeup_idx = cpfds.n; + if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { + mresult = CURLM_OUT_OF_MEMORY; + goto out; + } + } +#endif + curl_nfds = cpfds.n; /* what curl internally uses in cpfds */ /* Add external file descriptions from poll-like struct curl_waitfd */ for(i = 0; i < extra_nfds; i++) { @@ -1411,185 +1611,64 @@ } } -#ifdef USE_WINSOCK - /* Set the WSA events based on the collected pollds */ - for(i = 0; i < cpfds.n; i++) { - long mask = 0; - if(cpfds.pfds[i].events & POLLIN) - mask |= FD_READ | FD_ACCEPT | FD_CLOSE; - if(cpfds.pfds[i].events & POLLPRI) - mask |= FD_OOB; - if(cpfds.pfds[i].events & POLLOUT) { - mask |= FD_WRITE | FD_CONNECT | FD_CLOSE; - reset_socket_fdwrite(cpfds.pfds[i].fd); - } - if(mask) { - if(WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, mask) != 0) { - mresult = CURLM_OUT_OF_MEMORY; - goto out; - } - } - } -#endif - -#ifdef ENABLE_WAKEUP -#ifndef USE_WINSOCK - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(Curl_pollfds_add_sock(&cpfds, multi->wakeup_pair[0], POLLIN)) { - mresult = CURLM_OUT_OF_MEMORY; - goto out; - } - } -#endif -#endif - /* We check the internal timeout *AFTER* we collected all sockets to * poll. Collecting the sockets may install new timers by protocols * and connection filters. - * Use the shorter one of the internal and the caller requested timeout. */ + * Use the shorter one of the internal and the caller requested timeout. + * If we are called with `!extrawait` and multi_timeout() reports no + * timeouts exist, do not wait. */ multi_timeout(multi, &expire_time, &timeout_internal); if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) timeout_ms = (int)timeout_internal; if(data) - CURL_TRC_M(data, "multi_wait(fds=%d, timeout=%d) tinternal=%ld", + CURL_TRC_M(data, "multi_wait(fds=%u, timeout=%d) tinternal=%ld", cpfds.n, timeout_ms, timeout_internal); -#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(cpfds.n || use_wakeup) { -#else - if(cpfds.n) { -#endif - int pollrc; -#ifdef USE_WINSOCK - if(cpfds.n) /* pre-check with Winsock */ - pollrc = Curl_poll(cpfds.pfds, cpfds.n, 0); - else - pollrc = 0; -#else - pollrc = Curl_poll(cpfds.pfds, cpfds.n, timeout_ms); /* wait... */ -#endif - if(pollrc < 0) { - mresult = CURLM_UNRECOVERABLE_POLL; - goto out; - } - - if(pollrc > 0) { - retcode = pollrc; -#ifdef USE_WINSOCK - } - else { /* now wait... if not ready during the pre-check (pollrc == 0) */ - WSAWaitForMultipleEvents(1, &multi->wsa_event, FALSE, (DWORD)timeout_ms, - FALSE); - } - /* With Winsock, we have to run the following section unconditionally - to call WSAEventSelect(fd, event, 0) on all the sockets */ - { -#endif - /* copy revents results from the poll to the curl_multi_wait poll - struct, the bit values of the actual underlying poll() implementation - may not be the same as the ones in the public libcurl API! */ - for(i = 0; i < extra_nfds; i++) { - unsigned r = (unsigned)cpfds.pfds[curl_nfds + i].revents; - unsigned short mask = 0; -#ifdef USE_WINSOCK - curl_socket_t s = extra_fds[i].fd; - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(s, NULL, &wsa_events) == 0) { - if(wsa_events.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) - mask |= CURL_WAIT_POLLIN; - if(wsa_events.lNetworkEvents & (FD_WRITE | FD_CONNECT | FD_CLOSE)) - mask |= CURL_WAIT_POLLOUT; - if(wsa_events.lNetworkEvents & FD_OOB) - mask |= CURL_WAIT_POLLPRI; - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(s, multi->wsa_event, 0); - if(!pollrc) { - extra_fds[i].revents = (short)mask; - continue; - } -#endif - if(r & POLLIN) - mask |= CURL_WAIT_POLLIN; - if(r & POLLOUT) - mask |= CURL_WAIT_POLLOUT; - if(r & POLLPRI) - mask |= CURL_WAIT_POLLPRI; - extra_fds[i].revents = (short)mask; - } #ifdef USE_WINSOCK - /* Count up all our own sockets that had activity, - and remove them from the event. */ - for(i = 0; i < curl_nfds; ++i) { - wsa_events.lNetworkEvents = 0; - if(WSAEnumNetworkEvents(cpfds.pfds[i].fd, NULL, &wsa_events) == 0) { - if(ret && !pollrc && wsa_events.lNetworkEvents) - retcode++; - } - WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, 0); - } - WSAResetEvent(multi->wsa_event); + mresult = multi_winsock_select(multi, &cpfds, curl_nfds, + extra_fds, extra_nfds, + timeout_ms, extrawait, &nevents); #else + mresult = multi_posix_poll(multi, &cpfds, curl_nfds, + extra_fds, extra_nfds, + timeout_ms, extrawait, &nevents); +#endif + #ifdef ENABLE_WAKEUP - if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) { - if(cpfds.pfds[curl_nfds + extra_nfds].revents & POLLIN) { - (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); - /* do not count the wakeup socket into the returned value */ - retcode--; - } + if(nevents && (wakeup_idx >= 0)) { + if(cpfds.pfds[wakeup_idx].revents & POLLIN) { + (void)Curl_wakeup_consume(multi->wakeup_pair, TRUE); + /* do not count the wakeup socket into the returned value */ + nevents--; } -#endif -#endif } - } - - if(ret) - *ret = retcode; -#if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) - if(extrawait && !cpfds.n && !use_wakeup) { -#else - if(extrawait && !cpfds.n) { #endif - long sleep_ms = 0; - - /* Avoid busy-looping when there is nothing particular to wait for */ - multi_timeout(multi, &expire_time, &sleep_ms); - if(sleep_ms) { - if(sleep_ms > timeout_ms) - sleep_ms = timeout_ms; - /* when there are no easy handles in the multi, this holds a -1 - timeout */ - else if(sleep_ms < 0) - sleep_ms = timeout_ms; - curlx_wait_ms(sleep_ms); - } - } out: Curl_pollset_cleanup(&ps); Curl_pollfds_cleanup(&cpfds); + if(ret) + *ret = nevents; return mresult; } -CURLMcode curl_multi_wait(CURLM *multi, +CURLMcode curl_multi_wait(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, int *ret) { - return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE, - FALSE); + return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE); } -CURLMcode curl_multi_poll(CURLM *multi, +CURLMcode curl_multi_poll(CURLM *m, struct curl_waitfd extra_fds[], unsigned int extra_nfds, int timeout_ms, int *ret) { - return multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE, TRUE); + return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE); } CURLMcode curl_multi_wakeup(CURLM *m) @@ -1598,27 +1677,24 @@ it has to be careful only to access parts of the Curl_multi struct that are constant */ struct Curl_multi *multi = m; + CURLMcode mresult = CURLM_WAKEUP_FAILURE; /* GOOD_MULTI_HANDLE can be safely called */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; #ifdef ENABLE_WAKEUP -#ifdef USE_WINSOCK - if(WSASetEvent(multi->wsa_event)) - return CURLM_OK; -#else /* the wakeup_pair variable is only written during init and cleanup, making it safe to access from another thread after the init part and before cleanup */ - if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) { - if(Curl_wakeup_signal(multi->wakeup_pair)) - return CURLM_WAKEUP_FAILURE; - return CURLM_OK; - } + if(!Curl_wakeup_signal(multi->wakeup_pair)) + mresult = CURLM_OK; #endif +#ifdef USE_WINSOCK + if(WSASetEvent(multi->wsa_event)) + mresult = CURLM_OK; #endif - return CURLM_WAKEUP_FAILURE; + return mresult; } /* @@ -1660,7 +1736,6 @@ mresult = curl_multi_add_handle(multi, data); if(!mresult) { - struct SingleRequest *k = &data->req; CURLcode result; /* pass in NULL for 'conn' here since we do not want to init the @@ -1674,7 +1749,7 @@ /* take this handle to the perform state right away */ multistate(data, MSTATE_PERFORMING); Curl_attach_connection(data, conn); - k->keepon |= KEEP_RECV; /* setup to receive! */ + CURL_REQ_SET_RECV(data); } return mresult; } @@ -1732,13 +1807,9 @@ since = data->progress.t_startsingle; else since = data->progress.t_startop; - if(data->mstate == MSTATE_RESOLVING) - failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T - " milliseconds", - curlx_ptimediff_ms(Curl_pgrs_now(data), &since)); - else if(data->mstate == MSTATE_CONNECTING) - failf(data, "Connection timed out after %" FMT_TIMEDIFF_T - " milliseconds", + if(data->mstate == MSTATE_CONNECTING) + failf(data, "%s timed out after %" FMT_TIMEDIFF_T " milliseconds", + data->conn->bits.dns_resolved ? "Connection" : "Resolving", curlx_ptimediff_ms(Curl_pgrs_now(data), &since)); else { struct SingleRequest *k = &data->req; @@ -1867,7 +1938,7 @@ * multi_follow() handles the URL redirect magic. Pass in the 'newurl' string * as given by the remote server and set up the new URL to request. * - * This function DOES NOT FREE the given url. + * This function DOES NOT FREE the given URL. */ static CURLcode multi_follow(struct Curl_easy *data, const struct Curl_scheme *handler, @@ -1876,6 +1947,12 @@ { if(handler && handler->run->follow) return handler->run->follow(data, newurl, type); + + if(type == FOLLOW_RETRY) + /* Retries are generic and do not require protocol-specific redirect + handling. */ + return CURLE_OK; + return CURLE_TOO_MANY_REDIRECTS; } @@ -2224,56 +2301,6 @@ return mresult; } -static CURLMcode state_resolving(struct Curl_multi *multi, - struct Curl_easy *data, - bool *stream_errorp, - CURLcode *resultp) -{ - struct Curl_dns_entry *dns = NULL; - CURLcode result; - CURLMcode mresult = CURLM_OK; - - result = Curl_resolv_check(data, &dns); - CURL_TRC_DNS(data, "Curl_resolv_check() -> %d, %s", - result, dns ? "found" : "missing"); - /* Update sockets here, because the socket(s) may have been closed and the - application thus needs to be told, even if it is likely that the same - socket(s) will again be used further down. If the name has not yet been - resolved, it is likely that new sockets have been opened in an attempt to - contact another resolver. */ - mresult = Curl_multi_ev_assess_xfer(multi, data); - if(mresult) - return mresult; - - if(dns) { - bool connected; - /* Perform the next step in the connection phase, and then move on to the - WAITCONNECT state */ - result = Curl_once_resolved(data, dns, &connected); - - if(result) - /* if Curl_once_resolved() returns failure, the connection struct is - already freed and gone */ - data->conn = NULL; /* no more connection */ - else { - /* call again please so that we get the next socket setup */ - mresult = 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 mresult; -} - static CURLMcode state_connect(struct Curl_multi *multi, struct Curl_easy *data, CURLcode *resultp) @@ -2281,9 +2308,8 @@ /* 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 mresult = CURLM_OK; - CURLcode result = Curl_connect(data, &async, &connected); + CURLcode result = Curl_connect(data, &connected); if(result == CURLE_NO_CONNECTION_AVAILABLE) { /* There was no connection available. We will go to the pending state and wait for an available connection. */ @@ -2299,26 +2325,21 @@ process_pending_handles(data->multi); if(!result) { - 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! */ - mresult = CURLM_CALL_MULTI_PERFORM; + /* after the connect has been sent off, go WAITCONNECT unless the + protocol connect is already done and we can go directly to WAITDO or + DO! */ + mresult = CURLM_CALL_MULTI_PERFORM; - if(connected) { - if(!data->conn->bits.reuse && - Curl_conn_is_multiplex(data->conn, FIRSTSOCKET)) { - /* new connection, can multiplex, wake pending handles */ - process_pending_handles(data->multi); - } - multistate(data, MSTATE_PROTOCONNECT); + 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); } - else { - multistate(data, MSTATE_CONNECTING); - } + multistate(data, MSTATE_PROTOCONNECT); + } + else { + multistate(data, MSTATE_CONNECTING); } } *resultp = result; @@ -2367,7 +2388,7 @@ return result; } /* if there is still a connection to use, call the progress function */ - else if(data->conn) { + else if(data->conn && Curl_conn_is_connected(data->conn, FIRSTSOCKET)) { result = Curl_pgrsUpdate(data); if(result) { /* aborted due to progress callback return code must close the @@ -2424,6 +2445,8 @@ Curl_uint32_bset_remove(&multi->pending, data->mid); Curl_uint32_bset_add(&multi->msgsent, data->mid); --multi->xfers_alive; + if(!multi->xfers_alive) + multi_assess_wakeup(multi); } static CURLMcode multi_runsingle(struct Curl_multi *multi, @@ -2456,6 +2479,9 @@ Curl_uint32_bset_remove(&multi->dirty, data->mid); if(data == multi->admin) { +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_process(multi); +#endif Curl_cshutdn_perform(&multi->cshutdn, multi->admin, sigpipe_ctx); return CURLM_OK; } @@ -2519,15 +2545,14 @@ mresult = state_connect(multi, data, &result); break; - case MSTATE_RESOLVING: - /* awaiting an asynch name resolve to complete */ - mresult = state_resolving(multi, data, &stream_error, &result); - break; - case MSTATE_CONNECTING: /* awaiting a completion of an asynch TCP connect */ - DEBUGASSERT(data->conn); - if(!Curl_xfer_recv_is_paused(data)) { + if(!data->conn) { + DEBUGASSERT(0); + result = CURLE_FAILED_INIT; + break; + } + else if(!Curl_xfer_recv_is_paused(data)) { result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &connected); if(connected && !result) { if(!data->conn->bits.reuse && @@ -2540,6 +2565,7 @@ } else if(result) { /* failure detected */ + CURL_TRC_M(data, "connect failed -> %d", result); multi_posttransfer(data); multi_done(data, result, TRUE); stream_error = TRUE; @@ -2885,6 +2911,9 @@ } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)); } +#ifdef USE_RESOLV_THREADED + Curl_async_thrdd_multi_destroy(multi, !multi->quick_exit); +#endif Curl_cpool_destroy(&multi->cpool); Curl_cshutdn_destroy(&multi->cshutdn, multi->admin); if(multi->admin) { @@ -2906,11 +2935,10 @@ #ifdef USE_WINSOCK WSACloseEvent(multi->wsa_event); -#else +#endif #ifdef ENABLE_WAKEUP Curl_wakeup_destroy(multi->wakeup_pair); #endif -#endif multi_xfer_bufs_free(multi); Curl_mntfy_cleanup(multi); @@ -3148,6 +3176,14 @@ handles the case when the application asks libcurl to run the timeout prematurely. */ memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts)); + + /* Applications may set `socket_cb` *after* having added transfers + * first. *Then* kick off processing with a + * curl_multi_socket_action(TIMEOUT) afterwards. Make sure our + * admin handle registers its pollset with the callbacks present. */ + mresult = multi_assess_wakeup(multi); + if(mresult) + goto out; } multi_mark_expired_as_dirty(multi, multi_now(multi)); @@ -3255,6 +3291,11 @@ } case CURLMOPT_NETWORK_CHANGED: { long val = va_arg(param, long); + if(val & CURLMNWC_CLEAR_ALL) + /* In the beginning, all values available to set were 1 by mistake. We + converted this to mean "all", thus setting all the bits + automatically */ + val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS; if(val & CURLMNWC_CLEAR_DNS) { Curl_dnscache_clear(multi->admin); } @@ -3269,6 +3310,34 @@ case CURLMOPT_NOTIFYDATA: multi->ntfy.ntfy_cb_data = va_arg(param, void *); break; + case CURLMOPT_RESOLVE_THREADS_MAX: +#ifdef USE_RESOLV_THREADED + uarg = va_arg(param, long); + if((uarg <= 0) || (uarg > UINT32_MAX)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + else { + CURLcode result = Curl_async_thrdd_multi_set_props( + multi, 0, (uint32_t)uarg, 2000); + switch(result) { + case CURLE_OK: + mresult = CURLM_OK; + break; + case CURLE_BAD_FUNCTION_ARGUMENT: + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + break; + case CURLE_OUT_OF_MEMORY: + mresult = CURLM_OUT_OF_MEMORY; + break; + default: + mresult = CURLM_INTERNAL_ERROR; + break; + } + } +#endif + break; + case CURLMOPT_QUICK_EXIT: + multi->quick_exit = va_arg(param, long) ? 1 : 0; + break; default: mresult = CURLM_UNKNOWN_OPTION; break; @@ -3652,14 +3721,14 @@ } } -CURLMcode curl_multi_assign(CURLM *m, curl_socket_t s, - void *hashp) +CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, + void *sockp) { struct Curl_multi *multi = m; if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; - return Curl_multi_ev_assign(multi, s, hashp); + return Curl_multi_ev_assign(multi, sockfd, sockp); } static void move_pending_to_connect(struct Curl_multi *multi, @@ -3691,13 +3760,32 @@ */ static void process_pending_handles(struct Curl_multi *multi) { - uint32_t mid; + uint32_t mid = multi->last_pending_mid; + + if(mid) { + while(Curl_uint32_bset_next(&multi->pending, mid, &mid)) { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(data) { + move_pending_to_connect(multi, data); + multi->last_pending_mid = mid; + return; + } + /* transfer no longer known, should not happen */ + Curl_uint32_bset_remove(&multi->pending, mid); + DEBUGASSERT(0); + } + /* found no pending transfers with `mid` larger than `last_pending_mid`. + * Start at the beginning of the pending set again. */ + multi->last_pending_mid = 0; + } + if(Curl_uint32_bset_first(&multi->pending, &mid)) { do { struct Curl_easy *data = Curl_multi_get_easy(multi, mid); if(data) { move_pending_to_connect(multi, data); - break; + multi->last_pending_mid = mid; + return; } /* transfer no longer known, should not happen */ Curl_uint32_bset_remove(&multi->pending, mid); @@ -3941,13 +4029,13 @@ static void multi_xfer_bufs_free(struct Curl_multi *multi) { DEBUGASSERT(multi); - Curl_safefree(multi->xfer_buf); + curlx_safefree(multi->xfer_buf); multi->xfer_buf_len = 0; multi->xfer_buf_borrowed = FALSE; - Curl_safefree(multi->xfer_ulbuf); + curlx_safefree(multi->xfer_ulbuf); multi->xfer_ulbuf_len = 0; multi->xfer_ulbuf_borrowed = FALSE; - Curl_safefree(multi->xfer_sockbuf); + curlx_safefree(multi->xfer_sockbuf); multi->xfer_sockbuf_len = 0; multi->xfer_sockbuf_borrowed = FALSE; }
diff --git a/Utilities/cmcurl/lib/multi_ev.c b/Utilities/cmcurl/lib/multi_ev.c index 696a012..937e7ce 100644 --- a/Utilities/cmcurl/lib/multi_ev.c +++ b/Utilities/cmcurl/lib/multi_ev.c
@@ -253,7 +253,7 @@ DEBUGASSERT(entry->writers + entry->readers); CURL_TRC_M(data, "ev update fd=%" FMT_SOCKET_T ", action '%s%s' -> '%s%s'" - " (%d/%d r/w)", s, + " (%u/%u r/w)", s, (last_action & CURL_POLL_IN) ? "IN" : "", (last_action & CURL_POLL_OUT) ? "OUT" : "", (cur_action & CURL_POLL_IN) ? "IN" : "", @@ -603,10 +603,8 @@ struct Curl_easy *data) { DEBUGASSERT(!data->conn); /* transfer should have been detached */ - if(data != multi->admin) { - (void)mev_assess(multi, data, NULL); - Curl_meta_remove(data, CURL_META_MEV_POLLSET); - } + (void)mev_assess(multi, data, NULL); + Curl_meta_remove(data, CURL_META_MEV_POLLSET); } void Curl_multi_ev_conn_done(struct Curl_multi *multi,
diff --git a/Utilities/cmcurl/lib/multihandle.h b/Utilities/cmcurl/lib/multihandle.h index 7a56112..c5cdfbe 100644 --- a/Utilities/cmcurl/lib/multihandle.h +++ b/Utilities/cmcurl/lib/multihandle.h
@@ -27,7 +27,7 @@ #include "hash.h" #include "conncache.h" #include "cshutdn.h" -#include "hostip.h" +#include "dnscache.h" #include "multi_ev.h" #include "multi_ntfy.h" #include "psl.h" @@ -53,7 +53,6 @@ MSTATE_PENDING, /* no connections, waiting for one */ MSTATE_SETUP, /* start a new transfer */ MSTATE_CONNECT, /* resolve/connect has been sent off */ - MSTATE_RESOLVING, /* awaiting the resolve to finalize */ MSTATE_CONNECTING, /* awaiting the TCP connect to finalize */ MSTATE_PROTOCONNECT, /* initiate protocol connect procedure */ MSTATE_PROTOCONNECTING, /* completing the protocol-specific connect phase */ @@ -71,7 +70,7 @@ #define CURLPIPE_ANY (CURLPIPE_MULTIPLEX) -#ifndef CURL_DISABLE_SOCKETPAIR +#if !defined(CURL_DISABLE_SOCKETPAIR) && !defined(USE_WINSOCK) #define ENABLE_WAKEUP #endif @@ -109,6 +108,9 @@ struct Curl_dnscache dnscache; /* DNS cache */ struct Curl_ssl_scache *ssl_scache; /* TLS session pool */ +#ifdef USE_RESOLV_THREADED + struct curl_thrdq *resolv_thrdq; +#endif #ifdef USE_LIBPSL /* PSL cache. */ @@ -160,13 +162,12 @@ #ifdef USE_WINSOCK WSAEVENT wsa_event; /* Winsock event used for waits */ -#else +#endif #ifdef ENABLE_WAKEUP curl_socket_t wakeup_pair[2]; /* eventfd()/pipe()/socketpair() used for wakeup 0 is used for read, 1 is used for write */ #endif -#endif unsigned int max_concurrent_streams; unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of entries we are allowed to grow the connection @@ -174,6 +175,8 @@ #ifdef DEBUGBUILD unsigned int now_access_count; #endif + uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */ + uint32_t last_resolv_id; /* id of the last DNS resolve operation */ BIT(ipv6_works); BIT(multiplexing); /* multiplexing wanted */ BIT(recheckstate); /* see Curl_multi_connchanged */ @@ -187,6 +190,7 @@ BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */ BIT(xfer_ulbuf_borrowed); /* xfer_ulbuf is currently being borrowed */ BIT(xfer_sockbuf_borrowed); /* xfer_sockbuf is currently being borrowed */ + BIT(quick_exit); /* do not join threads on cleanup */ #ifdef DEBUGBUILD BIT(warned); /* true after user warned of DEBUGBUILD */ #endif
diff --git a/Utilities/cmcurl/lib/netrc.c b/Utilities/cmcurl/lib/netrc.c index 1073cc5..72d8fee 100644 --- a/Utilities/cmcurl/lib/netrc.c +++ b/Utilities/cmcurl/lib/netrc.c
@@ -72,35 +72,329 @@ { NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */ FILE *file = curlx_fopen(filename, FOPEN_READTEXT); - struct dynbuf linebuf; - curlx_dyn_init(&linebuf, MAX_NETRC_LINE); if(file) { - CURLcode result = CURLE_OK; - bool eof; - ret = NETRC_OK; - do { - const char *line; - result = Curl_get_line(&linebuf, file, &eof); - if(!result) { - line = curlx_dyn_ptr(&linebuf); - /* skip comments on load */ - curlx_str_passblanks(&line); - if(*line == '#') - continue; - result = curlx_dyn_add(filebuf, line); - } - if(result) { - curlx_dyn_free(filebuf); - ret = curl2netrc(result); + curlx_struct_stat stat; + if((curlx_fstat(fileno(file), &stat) == -1) || !S_ISDIR(stat.st_mode)) { + CURLcode result = CURLE_OK; + bool eof; + struct dynbuf linebuf; + curlx_dyn_init(&linebuf, MAX_NETRC_LINE); + ret = NETRC_OK; + do { + const char *line; + /* Curl_get_line always returns lines ending with a newline */ + result = Curl_get_line(&linebuf, file, &eof); + if(!result) { + line = curlx_dyn_ptr(&linebuf); + /* skip comments on load */ + curlx_str_passblanks(&line); + if(*line == '#') + continue; + result = curlx_dyn_add(filebuf, line); + } + if(result) { + curlx_dyn_free(filebuf); + ret = curl2netrc(result); + break; + } + } while(!eof); + curlx_dyn_free(&linebuf); + } + curlx_fclose(file); + } + return ret; +} + +/* bundled parser state to keep function signatures compact */ +struct netrc_state { + char *login; + char *password; + enum host_lookup_state state; + enum found_state keyword; + NETRCcode retcode; + unsigned char found; /* FOUND_LOGIN | FOUND_PASSWORD bits */ + bool our_login; + bool done; + bool specific_login; +}; + +/* + * Parse a quoted token starting after the opening '"'. Handles \n, \r, \t + * escape sequences. Advances *tok_endp past the closing '"'. + * + * Returns NETRC_OK or error. + */ +static NETRCcode netrc_quoted_token(const char **tok_endp, + struct dynbuf *token) +{ + bool escape = FALSE; + NETRCcode rc = NETRC_SYNTAX_ERROR; + const char *tok_end = *tok_endp; + tok_end++; /* pass the leading quote */ + while(*tok_end) { + CURLcode result; + char s = *tok_end; + if(escape) { + escape = FALSE; + switch(s) { + case 'n': + s = '\n'; + break; + case 'r': + s = '\r'; + break; + case 't': + s = '\t'; break; } - } while(!eof); + } + else if(s == '\\') { + escape = TRUE; + tok_end++; + continue; + } + else if(s == '\"') { + tok_end++; /* pass the ending quote */ + rc = NETRC_OK; + break; + } + result = curlx_dyn_addn(token, &s, 1); + if(result) { + *tok_endp = tok_end; + return curl2netrc(result); + } + tok_end++; } - curlx_dyn_free(&linebuf); - if(file) - curlx_fclose(file); - return ret; + *tok_endp = tok_end; + return rc; +} + +/* + * Gets the next token from the netrc buffer at *tokp. Writes the token into + * the 'token' dynbuf. Advances *tok_endp past the consumed token in the input + * buffer. Updates *statep for MACDEF newline handling. Sets *lineend = TRUE + * when the line is exhausted. + * + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_get_token(const char **tokp, + const char **tok_endp, + struct dynbuf *token, + enum host_lookup_state *statep, + bool *lineend) +{ + const char *tok = *tokp; + const char *tok_end; + + *lineend = FALSE; + curlx_dyn_reset(token); + curlx_str_passblanks(&tok); + + /* tok is first non-space letter */ + if(*statep == MACDEF) { + if((*tok == '\n') || (*tok == '\r')) + *statep = NOTHING; /* end of macro definition */ + *lineend = TRUE; + *tokp = tok; + return NETRC_OK; + } + + if(!*tok || (*tok == '\n')) { + /* end of line */ + *lineend = TRUE; + *tokp = tok; + return NETRC_OK; + } + + tok_end = tok; + if(*tok == '\"') { + /* quoted string */ + NETRCcode ret = netrc_quoted_token(&tok_end, token); + if(ret) + return ret; + } + else { + /* unquoted token */ + size_t len = 0; + CURLcode result; + while(*tok_end > ' ') { + tok_end++; + len++; + } + if(!len) + return NETRC_SYNTAX_ERROR; + result = curlx_dyn_addn(token, tok, len); + if(result) + return curl2netrc(result); + } + + *tok_endp = tok_end; + + if(curlx_dyn_len(token)) + *tokp = curlx_dyn_ptr(token); + else + /* set it to blank to avoid NULL */ + *tokp = ""; + + return NETRC_OK; +} + +/* + * Reset parser for a new machine entry. Frees password and optionally login + * if it was not user-specified. + */ +static void netrc_new_machine(struct netrc_state *ns) +{ + ns->keyword = NONE; + ns->found = 0; + ns->our_login = FALSE; + curlx_safefree(ns->password); + if(!ns->specific_login) + curlx_safefree(ns->login); +} + +/* + * Process a parsed token through the HOSTVALID state machine branch. This + * handles login/password values and keyword transitions for the matched host. + * + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_hostvalid(struct netrc_state *ns, const char *tok) +{ + if(ns->keyword == LOGIN) { + if(ns->specific_login) + ns->our_login = !Curl_timestrcmp(ns->login, tok); + else { + ns->our_login = TRUE; + curlx_free(ns->login); + ns->login = curlx_strdup(tok); + if(!ns->login) + return NETRC_OUT_OF_MEMORY; + } + ns->found |= FOUND_LOGIN; + ns->keyword = NONE; + } + else if(ns->keyword == PASSWORD) { + curlx_free(ns->password); + ns->password = curlx_strdup(tok); + if(!ns->password) + return NETRC_OUT_OF_MEMORY; + ns->found |= FOUND_PASSWORD; + ns->keyword = NONE; + } + else if(curl_strequal("login", tok)) + ns->keyword = LOGIN; + else if(curl_strequal("password", tok)) + ns->keyword = PASSWORD; + else if(curl_strequal("machine", tok)) { + /* a new machine here */ + + if(ns->found & FOUND_PASSWORD && + /* a password was provided for this host */ + + ((!ns->specific_login || ns->our_login) || + /* either there was no specific login to search for, or this + is the specific one we wanted */ + (ns->specific_login && !(ns->found & FOUND_LOGIN)))) { + /* or we look for a specific login, but that was not specified */ + + ns->done = TRUE; + return NETRC_OK; + } + + ns->state = HOSTFOUND; + netrc_new_machine(ns); + } + else if(curl_strequal("default", tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + netrc_new_machine(ns); + } + if((ns->found == (FOUND_PASSWORD | FOUND_LOGIN)) && ns->our_login) + ns->done = TRUE; + return NETRC_OK; +} + +/* + * Process one parsed token through the netrc state + * machine. Updates the parser state in *ns. + * Returns NETRC_OK or an error code. + */ +static NETRCcode netrc_handle_token(struct netrc_state *ns, + const char *tok, + const char *host) +{ + switch(ns->state) { + case NOTHING: + if(curl_strequal("macdef", tok)) + ns->state = MACDEF; + else if(curl_strequal("machine", tok)) { + ns->state = HOSTFOUND; + netrc_new_machine(ns); + } + else if(curl_strequal("default", tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + } + break; + case MACDEF: + if(!*tok) + ns->state = NOTHING; + break; + case HOSTFOUND: + if(curl_strequal(host, tok)) { + ns->state = HOSTVALID; + ns->retcode = NETRC_OK; + } + else + ns->state = NOTHING; + break; + case HOSTVALID: + return netrc_hostvalid(ns, tok); + } + return NETRC_OK; +} + +/* + * Finalize the parse result: fill in defaults and free + * resources on error. + */ +static NETRCcode netrc_finalize(struct netrc_state *ns, + char **loginp, + char **passwordp, + struct store_netrc *store) +{ + NETRCcode retcode = ns->retcode; + if(!retcode) { + if(!ns->password && ns->our_login) { + /* success without a password, set a blank one */ + ns->password = curlx_strdup(""); + if(!ns->password) + retcode = NETRC_OUT_OF_MEMORY; + } + else if(!ns->login && !ns->password) + /* a default with no credentials */ + retcode = NETRC_NO_MATCH; + } + if(!retcode) { + /* success */ + if(!ns->specific_login) + *loginp = ns->login; + + /* netrc_finalize() can return a password even when specific_login is set + but our_login is false (e.g., host matched but the requested login + never matched). See test 685. */ + *passwordp = ns->password; + } + else { + curlx_dyn_free(&store->filebuf); + store->loaded = FALSE; + if(!ns->specific_login) + curlx_free(ns->login); + curlx_free(ns->password); + } + return retcode; } /* @@ -108,23 +402,20 @@ */ static NETRCcode parsenetrc(struct store_netrc *store, const char *host, - char **loginp, /* might point to a username */ + char **loginp, char **passwordp, const char *netrcfile) { - NETRCcode retcode = NETRC_NO_MATCH; - char *login = *loginp; - char *password = NULL; - bool specific_login = !!login; /* points to something */ - enum host_lookup_state state = NOTHING; - enum found_state keyword = NONE; - unsigned char found = 0; /* login + password found bits, as they can come in - any order */ - bool our_login = FALSE; /* found our login name */ - bool done = FALSE; const char *netrcbuffer; struct dynbuf token; struct dynbuf *filebuf = &store->filebuf; + struct netrc_state ns; + + memset(&ns, 0, sizeof(ns)); + ns.retcode = NETRC_NO_MATCH; + ns.login = *loginp; + ns.specific_login = !!ns.login; + DEBUGASSERT(!*passwordp); curlx_dyn_init(&token, MAX_NETRC_TOKEN); @@ -137,195 +428,32 @@ netrcbuffer = curlx_dyn_ptr(filebuf); - while(!done) { + while(!ns.done) { const char *tok = netrcbuffer; - while(tok && !done) { + while(tok && !ns.done) { const char *tok_end; - bool quoted; - curlx_dyn_reset(&token); - curlx_str_passblanks(&tok); - /* tok is first non-space letter */ - if(state == MACDEF) { - if((*tok == '\n') || (*tok == '\r')) - state = NOTHING; /* end of macro definition */ + bool lineend; + NETRCcode ret; + + ret = netrc_get_token(&tok, &tok_end, &token, &ns.state, &lineend); + if(ret) { + ns.retcode = ret; + goto out; } - - if(!*tok || (*tok == '\n')) - /* end of line */ + if(lineend) break; - /* leading double-quote means quoted string */ - quoted = (*tok == '\"'); - - tok_end = tok; - if(!quoted) { - size_t len = 0; - CURLcode result; - while(*tok_end > ' ') { - tok_end++; - len++; - } - if(!len) { - retcode = NETRC_SYNTAX_ERROR; - goto out; - } - result = curlx_dyn_addn(&token, tok, len); - if(result) { - retcode = curl2netrc(result); - goto out; - } + ret = netrc_handle_token(&ns, tok, host); + if(ret) { + ns.retcode = ret; + goto out; } - else { - bool escape = FALSE; - bool endquote = FALSE; - tok_end++; /* pass the leading quote */ - while(*tok_end) { - CURLcode result; - char s = *tok_end; - if(escape) { - escape = FALSE; - switch(s) { - case 'n': - s = '\n'; - break; - case 'r': - s = '\r'; - break; - case 't': - s = '\t'; - break; - } - } - else if(s == '\\') { - escape = TRUE; - tok_end++; - continue; - } - else if(s == '\"') { - tok_end++; /* pass the ending quote */ - endquote = TRUE; - break; - } - result = curlx_dyn_addn(&token, &s, 1); - if(result) { - retcode = curl2netrc(result); - goto out; - } - tok_end++; - } - if(escape || !endquote) { - /* bad syntax, get out */ - retcode = NETRC_SYNTAX_ERROR; - goto out; - } - } - - if(curlx_dyn_len(&token)) - tok = curlx_dyn_ptr(&token); - else - /* since tok might actually be NULL for no content, set it to blank - to avoid having to deal with it being NULL */ - tok = ""; - - switch(state) { - case NOTHING: - if(curl_strequal("macdef", tok)) - /* Define a macro. A macro is defined with the specified name; its - contents begin with the next .netrc line and continue until a - null line (consecutive new-line characters) is encountered. */ - state = MACDEF; - else if(curl_strequal("machine", tok)) { - /* the next tok is the machine name, this is in itself the delimiter - that starts the stuff entered for this machine, after this we - need to search for 'login' and 'password'. */ - state = HOSTFOUND; - keyword = NONE; - found = 0; - our_login = FALSE; - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - else if(curl_strequal("default", tok)) { - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - } - break; - case MACDEF: - if(!*tok) - state = NOTHING; - break; - case HOSTFOUND: - if(curl_strequal(host, tok)) { - /* and yes, this is our host! */ - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - } - else - /* not our host */ - state = NOTHING; - break; - case HOSTVALID: - /* we are now parsing sub-keywords concerning "our" host */ - if(keyword == LOGIN) { - if(specific_login) - our_login = !Curl_timestrcmp(login, tok); - else { - our_login = TRUE; - curlx_free(login); - login = curlx_strdup(tok); - if(!login) { - retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */ - goto out; - } - } - found |= FOUND_LOGIN; - keyword = NONE; - } - else if(keyword == PASSWORD) { - curlx_free(password); - password = curlx_strdup(tok); - if(!password) { - retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */ - goto out; - } - if(!specific_login || our_login) - found |= FOUND_PASSWORD; - keyword = NONE; - } - else if(curl_strequal("login", tok)) - keyword = LOGIN; - else if(curl_strequal("password", tok)) - keyword = PASSWORD; - else if(curl_strequal("machine", tok)) { - /* a new machine here */ - if(found & FOUND_PASSWORD) { - done = TRUE; - break; - } - state = HOSTFOUND; - keyword = NONE; - found = 0; - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - else if(curl_strequal("default", tok)) { - state = HOSTVALID; - retcode = NETRC_OK; /* we did find our host */ - Curl_safefree(password); - if(!specific_login) - Curl_safefree(login); - } - if((found == (FOUND_PASSWORD | FOUND_LOGIN)) && our_login) { - done = TRUE; - break; - } - break; - } /* switch (state) */ + /* tok_end cannot point to a null byte here since lines are always + newline terminated */ + DEBUGASSERT(*tok_end); tok = ++tok_end; } - if(!done) { + if(!ns.done) { const char *nl = NULL; if(tok) nl = strchr(tok, '\n'); @@ -338,32 +466,7 @@ out: curlx_dyn_free(&token); - if(!retcode) { - if(!password && our_login) { - /* success without a password, set a blank one */ - password = curlx_strdup(""); - if(!password) - retcode = NETRC_OUT_OF_MEMORY; /* out of memory */ - } - else if(!login && !password) - /* a default with no credentials */ - retcode = NETRC_NO_MATCH; - } - if(!retcode) { - /* success */ - if(!specific_login) - *loginp = login; - *passwordp = password; - } - else { - curlx_dyn_free(filebuf); - store->loaded = FALSE; - if(!specific_login) - curlx_free(login); - curlx_free(password); - } - - return retcode; + return netrc_finalize(&ns, loginp, passwordp, store); } const char *Curl_netrc_strerror(NETRCcode ret)
diff --git a/Utilities/cmcurl/lib/netrc.h b/Utilities/cmcurl/lib/netrc.h index 061e1e4..90318c2 100644 --- a/Utilities/cmcurl/lib/netrc.h +++ b/Utilities/cmcurl/lib/netrc.h
@@ -58,7 +58,6 @@ */ #else /* disabled */ -#define Curl_parsenetrc(a, b, c, d, e, f) 1 #define Curl_netrc_init(x) #define Curl_netrc_cleanup(x) #endif
diff --git a/Utilities/cmcurl/lib/noproxy.c b/Utilities/cmcurl/lib/noproxy.c index ee03fd3..05c59a0 100644 --- a/Utilities/cmcurl/lib/noproxy.c +++ b/Utilities/cmcurl/lib/noproxy.c
@@ -38,12 +38,17 @@ #endif /* - * Curl_cidr4_match() returns TRUE if the given IPv4 address is within the + * cidr4_match() returns TRUE if the given IPv4 address is within the * specified CIDR address range. + * + * @unittest 1614 */ -UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits) +UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits); +UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ + const char *network, /* 1.2.3.4 address */ + unsigned int bits) { unsigned int address = 0; unsigned int check = 0; @@ -74,9 +79,11 @@ return address == check; } -UNITTEST bool Curl_cidr6_match(const char *ipv6, - const char *network, - unsigned int bits) +/* @unittest 1614 */ +UNITTEST bool cidr6_match(const char *ipv6, const char *network, + unsigned int bits); +UNITTEST bool cidr6_match(const char *ipv6, const char *network, + unsigned int bits) { #ifdef USE_IPV6 unsigned int bytes; @@ -170,9 +177,9 @@ *slash = 0; /* null-terminate there */ } if(type == TYPE_IPV6) - return Curl_cidr6_match(name, checkip, bits); + return cidr6_match(name, checkip, bits); else - return Curl_cidr4_match(name, checkip, bits); + return cidr4_match(name, checkip, bits); } /****************************************************************
diff --git a/Utilities/cmcurl/lib/noproxy.h b/Utilities/cmcurl/lib/noproxy.h index 71ae7ea..e16c139 100644 --- a/Utilities/cmcurl/lib/noproxy.h +++ b/Utilities/cmcurl/lib/noproxy.h
@@ -26,17 +26,6 @@ #include "curl_setup.h" #ifndef CURL_DISABLE_PROXY - -#ifdef UNITTESTS - -UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */ - const char *network, /* 1.2.3.4 address */ - unsigned int bits); -UNITTEST bool Curl_cidr6_match(const char *ipv6, - const char *network, - unsigned int bits); -#endif - bool Curl_check_noproxy(const char *name, const char *no_proxy); #endif
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c index 95f7681..48bf5b7 100644 --- a/Utilities/cmcurl/lib/openldap.c +++ b/Utilities/cmcurl/lib/openldap.c
@@ -444,18 +444,18 @@ struct connectdata *conn = data->conn; if(conn) { struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - CURLcode err = CURLE_RECV_ERROR; + CURLcode result = CURLE_RECV_ERROR; size_t nread; if(!li) { SET_SOCKERRNO(SOCKEINVAL); return -1; } - err = (li->recv)(data, FIRSTSOCKET, buf, len, &nread); - if(err == CURLE_AGAIN) { + result = (li->recv)(data, FIRSTSOCKET, buf, len, &nread); + if(result == CURLE_AGAIN) { SET_SOCKERRNO(SOCKEWOULDBLOCK); } - ret = err ? -1 : (ber_slen_t)nread; + ret = result ? -1 : (ber_slen_t)nread; } } return ret; @@ -470,18 +470,18 @@ struct connectdata *conn = data->conn; if(conn) { struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); - CURLcode err = CURLE_SEND_ERROR; + CURLcode result = CURLE_SEND_ERROR; size_t nwritten; if(!li) { SET_SOCKERRNO(SOCKEINVAL); return -1; } - err = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten); - if(err == CURLE_AGAIN) { + result = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten); + if(result == CURLE_AGAIN) { SET_SOCKERRNO(SOCKEWOULDBLOCK); } - ret = err ? -1 : (ber_slen_t)nwritten; + ret = result ? -1 : (ber_slen_t)nwritten; } } return ret; @@ -614,11 +614,10 @@ if(result) goto out; - hosturl = curl_maprintf("%s://%s%s%s:%d", + hosturl = curl_maprintf("%s://%s:%d", conn->scheme->name, - conn->bits.ipv6_ip ? "[" : "", - conn->host.name, - conn->bits.ipv6_ip ? "]" : "", + (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name, conn->remote_port); if(!hosturl) { result = CURLE_OUT_OF_MEMORY; @@ -694,13 +693,19 @@ static CURLcode oldap_state_mechs_resp(struct Curl_easy *data, LDAPMessage *msg, int code) { - struct connectdata *conn = data->conn; - struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + struct connectdata *conn; + struct ldapconninfo *li; int rc; BerElement *ber = NULL; CURLcode result = CURLE_OK; struct berval bv, *bvals; + if(!data) + return CURLE_FAILED_INIT; + + conn = data->conn; + li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); + if(!li) return CURLE_FAILED_INIT; switch(ldap_msgtype(msg)) { @@ -1276,7 +1281,7 @@ oldap_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ };
diff --git a/Utilities/cmcurl/lib/parsedate.c b/Utilities/cmcurl/lib/parsedate.c index 7efedec..ce358bd 100644 --- a/Utilities/cmcurl/lib/parsedate.c +++ b/Utilities/cmcurl/lib/parsedate.c
@@ -25,6 +25,7 @@ #include "parsedate.h" #include "curlx/strparse.h" +#include "curlx/strcopy.h" /* A brief summary of the date string formats this parser groks: @@ -98,9 +99,7 @@ #ifndef CURL_DISABLE_PARSEDATE #define PARSEDATE_LATER 1 -#if defined(HAVE_TIME_T_UNSIGNED) || (SIZEOF_TIME_T < 5) #define PARSEDATE_SOONER 2 -#endif static const char * const weekday[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" @@ -108,89 +107,83 @@ struct tzinfo { char name[5]; - int offset; /* +/- in minutes */ + int16_t offset; /* +/- in minutes */ }; -/* Here's a bunch of frequently used time zone names. These were supported - by the old getdate parser. */ #define tDAYZONE (-60) /* offset for daylight savings time */ + +/* alpha-sorted list of time zones */ static const struct tzinfo tz[] = { - { "GMT", 0 }, /* Greenwich Mean */ - { "UT", 0 }, /* Universal Time */ - { "UTC", 0 }, /* Universal (Coordinated) */ - { "WET", 0 }, /* Western European */ - { "BST", 0 + tDAYZONE }, /* British Summer */ - { "WAT", 60 }, /* West Africa */ - { "AST", 240 }, /* Atlantic Standard */ + { "A", -1 * 60 }, /* Alpha */ { "ADT", 240 + tDAYZONE }, /* Atlantic Daylight */ - { "EST", 300 }, /* Eastern Standard */ - { "EDT", 300 + tDAYZONE }, /* Eastern Daylight */ - { "CST", 360 }, /* Central Standard */ - { "CDT", 360 + tDAYZONE }, /* Central Daylight */ - { "MST", 420 }, /* Mountain Standard */ - { "MDT", 420 + tDAYZONE }, /* Mountain Daylight */ - { "PST", 480 }, /* Pacific Standard */ - { "PDT", 480 + tDAYZONE }, /* Pacific Daylight */ - { "YST", 540 }, /* Yukon Standard */ - { "YDT", 540 + tDAYZONE }, /* Yukon Daylight */ - { "HST", 600 }, /* Hawaii Standard */ - { "HDT", 600 + tDAYZONE }, /* Hawaii Daylight */ - { "CAT", 600 }, /* Central Alaska */ { "AHST", 600 }, /* Alaska-Hawaii Standard */ - { "NT", 660 }, /* Nome */ /* spellchecker:disable-line */ - { "IDLW", 720 }, /* International Date Line West */ + { "AST", 240 }, /* Atlantic Standard */ + { "B", -2 * 60 }, /* Bravo */ + { "BST", 0 + tDAYZONE }, /* British Summer */ + { "C", -3 * 60 }, /* Charlie */ + { "CAT", 600 }, /* Central Alaska */ + { "CCT", -480 }, /* China Coast, USSR Zone 7 */ + { "CDT", 360 + tDAYZONE }, /* Central Daylight */ + { "CEST", -60 + tDAYZONE }, /* Central European Summer */ { "CET", -60 }, /* Central European */ + { "CST", 360 }, /* Central Standard */ + { "D", -4 * 60 }, /* Delta */ + { "E", -5 * 60 }, /* Echo */ + { "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */ + { "EAST", -600 }, /* Eastern Australian Standard */ + { "EDT", 300 + tDAYZONE }, /* Eastern Daylight */ + { "EET", -120 }, /* Eastern Europe, USSR Zone 1 */ + { "EST", 300 }, /* Eastern Standard */ + { "F", -6 * 60 }, /* Foxtrot */ + { "FST", -60 + tDAYZONE }, /* French Summer */ + { "FWT", -60 }, /* French Winter */ + { "G", -7 * 60 }, /* Golf */ + { "GMT", 0 }, /* Greenwich Mean */ + { "GST", -600 }, /* Guam Standard, USSR Zone 9 */ + { "H", -8 * 60 }, /* Hotel */ + { "HDT", 600 + tDAYZONE }, /* Hawaii Daylight */ + { "HST", 600 }, /* Hawaii Standard */ + { "I", -9 * 60 }, /* India */ + { "IDLE", -720 }, /* International Date Line East */ + { "IDLW", 720 }, /* International Date Line West */ + { "JST", -540 }, /* Japan Standard, USSR Zone 8 */ + { "K", -10 * 60 }, /* Kilo */ + { "L", -11 * 60 }, /* Lima */ + { "M", -12 * 60 }, /* Mike */ + { "MDT", 420 + tDAYZONE }, /* Mountain Daylight */ + { "MEST", -60 + tDAYZONE }, /* Middle European Summer */ + { "MESZ", -60 + tDAYZONE }, /* Middle European Summer */ { "MET", -60 }, /* Middle European */ { "MEWT", -60 }, /* Middle European Winter */ - { "MEST", -60 + tDAYZONE }, /* Middle European Summer */ - { "CEST", -60 + tDAYZONE }, /* Central European Summer */ - { "MESZ", -60 + tDAYZONE }, /* Middle European Summer */ - { "FWT", -60 }, /* French Winter */ - { "FST", -60 + tDAYZONE }, /* French Summer */ - { "EET", -120 }, /* Eastern Europe, USSR Zone 1 */ + { "MST", 420 }, /* Mountain Standard */ + { "N", 60 }, /* November */ + { "NT", 660 }, /* Nome */ /* spellchecker:disable-line */ + { "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */ + { "NZST", -720 }, /* New Zealand Standard */ + { "NZT", -720 }, /* New Zealand */ + { "O", 2 * 60 }, /* Oscar */ + { "P", 3 * 60 }, /* Papa */ + { "PDT", 480 + tDAYZONE }, /* Pacific Daylight */ + { "PST", 480 }, /* Pacific Standard */ + { "Q", 4 * 60 }, /* Quebec */ + { "R", 5 * 60 }, /* Romeo */ + { "S", 6 * 60 }, /* Sierra */ + { "T", 7 * 60 }, /* Tango */ + { "U", 8 * 60 }, /* Uniform */ + { "UT", 0 }, /* Universal Time */ + { "UTC", 0 }, /* Universal (Coordinated) */ + { "V", 9 * 60 }, /* Victor */ + { "W", 10 * 60 }, /* Whiskey */ + { "WADT", -420 + tDAYZONE }, /* West Australian Daylight */ { "WAST", -420 }, /* spellchecker:disable-line */ /* West Australian Standard */ - { "WADT", -420 + tDAYZONE }, /* West Australian Daylight */ - { "CCT", -480 }, /* China Coast, USSR Zone 7 */ - { "JST", -540 }, /* Japan Standard, USSR Zone 8 */ - { "EAST", -600 }, /* Eastern Australian Standard */ - { "EADT", -600 + tDAYZONE }, /* Eastern Australian Daylight */ - { "GST", -600 }, /* Guam Standard, USSR Zone 9 */ - { "NZT", -720 }, /* New Zealand */ - { "NZST", -720 }, /* New Zealand Standard */ - { "NZDT", -720 + tDAYZONE }, /* New Zealand Daylight */ - { "IDLE", -720 }, /* International Date Line East */ - /* Next up: Military timezone names. RFC822 allowed these, but (as noted in - RFC 1123) had their signs wrong. Here we use the correct signs to match - actual military usage. - */ - { "A", 1 * 60 }, /* Alpha */ - { "B", 2 * 60 }, /* Bravo */ - { "C", 3 * 60 }, /* Charlie */ - { "D", 4 * 60 }, /* Delta */ - { "E", 5 * 60 }, /* Echo */ - { "F", 6 * 60 }, /* Foxtrot */ - { "G", 7 * 60 }, /* Golf */ - { "H", 8 * 60 }, /* Hotel */ - { "I", 9 * 60 }, /* India */ - /* "J", Juliet is not used as a timezone, to indicate the observer's local - time */ - { "K", 10 * 60 }, /* Kilo */ - { "L", 11 * 60 }, /* Lima */ - { "M", 12 * 60 }, /* Mike */ - { "N", -1 * 60 }, /* November */ - { "O", -2 * 60 }, /* Oscar */ - { "P", -3 * 60 }, /* Papa */ - { "Q", -4 * 60 }, /* Quebec */ - { "R", -5 * 60 }, /* Romeo */ - { "S", -6 * 60 }, /* Sierra */ - { "T", -7 * 60 }, /* Tango */ - { "U", -8 * 60 }, /* Uniform */ - { "V", -9 * 60 }, /* Victor */ - { "W", -10 * 60 }, /* Whiskey */ - { "X", -11 * 60 }, /* X-ray */ - { "Y", -12 * 60 }, /* Yankee */ - { "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */ + { "WAT", 60 }, /* West Africa */ + { "WET", 0 }, /* Western European */ + { "X", 11 * 60 }, /* X-ray */ + { "Y", 12 * 60 }, /* Yankee */ + { "YDT", 540 + tDAYZONE }, /* Yukon Daylight */ + { "YST", 540 }, /* Yukon Standard */ + { "Z", 0 }, /* Zulu, zero meridian, a.k.a. UTC */ }; /* returns: @@ -233,22 +226,24 @@ return -1; /* return the offset or -1, no real offset is -1 */ } -/* return the time zone offset between GMT and the input one, in number - of seconds or -1 if the timezone was not found/legal */ +static int tzcompare(const void *m1, const void *m2) +{ + const struct tzinfo *tz1 = m1; + const struct tzinfo *tz2 = m2; + return strcmp(tz1->name, tz2->name); +} +/* return the time zone offset between GMT and the input one, in number of + seconds or -1 if the timezone was not found/legal */ static int checktz(const char *check, size_t len) { - unsigned int i; - const struct tzinfo *what = tz; - if(len > 4) /* longer than any valid timezone */ - return -1; - - for(i = 0; i < CURL_ARRAYSIZE(tz); i++) { - size_t ilen = strlen(what->name); - if((ilen == len) && - curl_strnequal(check, what->name, len)) + if(len <= 4) { + const struct tzinfo *what; + struct tzinfo find; + curlx_strcopy(find.name, sizeof(find.name), check, len); + what = bsearch(&find, tz, CURL_ARRAYSIZE(tz), sizeof(tz[0]), tzcompare); + if(what) return what->offset * 60; - what++; } return -1; } @@ -260,29 +255,42 @@ (*date)++; } +/* each field is exactly -1 when unknown */ +struct when { + int wday; /* day of the week, 0-6 (mon-sun) */ + int mon; /* month of the year, 0-11 */ + int mday; /* day of month, 1 - 31 */ + int hour; /* hour of day, 0 - 23 */ + int min; /* minute of hour, 0 - 59 */ + int sec; /* second of minute, 0 - 60 (leap second) */ + int year; /* year, >= 1583 */ + int tzoff; /* time zone offset in seconds */ +}; + enum assume { DATE_MDAY, DATE_YEAR, DATE_TIME }; +/* (1969 / 4) - (1969 / 100) + (1969 / 400) = 492 - 19 + 4 = 477 */ +#define LEAP_DAYS_BEFORE_1969 477 + /* * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to * mktime but for GMT only. */ -static time_t time2epoch(int sec, int min, int hour, - int mday, int mon, int year) +static curl_off_t time2epoch(struct when *w) { - static const int month_days_cumulative[12] = { + static const int cumulative_days[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - int leap_days = year - (mon <= 1); - leap_days = ((leap_days / 4) - (leap_days / 100) + (leap_days / 400) - - (1969 / 4) + (1969 / 100) - (1969 / 400)); - return ((((((((time_t)(year - 1970) * 365) - + leap_days + month_days_cumulative[mon] + mday - 1) * 24) - + hour) * 60) - + min) * 60) + sec; + int y = w->year - (w->mon <= 1); + int leap_days = (y / 4) - (y / 100) + (y / 400) - LEAP_DAYS_BEFORE_1969; + curl_off_t days = (curl_off_t)(w->year - 1970) * 365 + leap_days + + cumulative_days[w->mon] + w->mday - 1; + + return (((days * 24 + w->hour) * 60 + w->min) * 60) + w->sec; } /* Returns the value of a single-digit or two-digit decimal number, return @@ -300,8 +308,7 @@ } /* HH:MM:SS or HH:MM and accept single-digits too */ -static bool match_time(const char *date, - int *h, int *m, int *s, char **endp) +static bool match_time(const char *date, struct when *w, char **endp) { const char *p; int hh, mm, ss = 0; @@ -324,9 +331,9 @@ } return FALSE; /* not a time string */ match: - *h = hh; - *m = mm; - *s = ss; + w->hour = hh; + w->min = mm; + w->sec = ss; *endp = (char *)CURL_UNCONST(p); return TRUE; } @@ -345,209 +352,221 @@ /* Wednesday is the longest name this parser knows about */ #define NAME_LEN 12 +static void initwhen(struct when *w) +{ + w->wday = w->mon = w->mday = w->hour = w->min = w->sec = w->year = w->tzoff = + -1; +} + +static int datestring(const char **datep, struct when *w) +{ + /* a name coming up */ + size_t len = 0; + const char *p = *datep; + bool found = FALSE; + while(ISALPHA(*p) && (len < NAME_LEN)) { + p++; + len++; + } + + if(len != NAME_LEN) { + if(w->wday == -1) { + w->wday = checkday(*datep, len); + if(w->wday != -1) + found = TRUE; + } + if(!found && (w->mon == -1)) { + w->mon = checkmonth(*datep, len); + if(w->mon != -1) + found = TRUE; + } + + if(!found && (w->tzoff == -1)) { + /* this must be a time zone string */ + w->tzoff = checktz(*datep, len); + if(w->tzoff != -1) + found = TRUE; + } + } + if(!found) + return PARSEDATE_FAIL; /* bad string */ + + *datep += len; + return PARSEDATE_OK; +} + +static int datenum(const char *indate, const char **datep, struct when *w, + enum assume *dignextp) +{ + /* a digit */ + unsigned int val; + char *end; + const char *date = *datep; + enum assume dignext = *dignextp; + + if((w->sec == -1) && match_time(date, w, &end)) { + /* time stamp */ + date = end; + } + else { + bool found = FALSE; + curl_off_t lval; + int num_digits = 0; + const char *p = *datep; + if(curlx_str_number(&p, &lval, 99999999)) + return PARSEDATE_FAIL; + + /* we know num_digits cannot be larger than 8 */ + num_digits = (int)(p - *datep); + val = (unsigned int)lval; + + if((w->tzoff == -1) && + (num_digits == 4) && + (val <= 1400) && + (indate < date) && + (date[-1] == '+' || date[-1] == '-')) { + /* four digits and a value less than or equal to 1400 (to take into + account all sorts of funny time zone diffs) and it is preceded + with a plus or minus. This is a time zone indication. 1400 is + picked since +1300 is frequently used and +1400 is mentioned as + an edge number in the document "ISO C 200X Proposal: Timezone + Functions" at http://david.tribble.com/text/c0xtimezone.html If + anyone has a more authoritative source for the exact maximum time + zone offsets, please speak up! */ + found = TRUE; + w->tzoff = ((val / 100 * 60) + (val % 100)) * 60; + + /* the + and - prefix indicates the local time compared to GMT, + this we need their reversed math to get what we want */ + w->tzoff = date[-1] == '+' ? -w->tzoff : w->tzoff; + } + + else if((num_digits == 8) && (w->year == -1) && + (w->mon == -1) && (w->mday == -1)) { + /* 8 digits, no year, month or day yet. This is YYYYMMDD */ + found = TRUE; + w->year = val / 10000; + w->mon = ((val % 10000) / 100) - 1; /* month is 0 - 11 */ + w->mday = val % 100; + } + + if(!found && (dignext == DATE_MDAY) && (w->mday == -1)) { + if((val > 0) && (val < 32)) { + w->mday = val; + found = TRUE; + } + dignext = DATE_YEAR; + } + + if(!found && (dignext == DATE_YEAR) && (w->year == -1)) { + w->year = val; + found = TRUE; + if(w->year < 100) { + if(w->year > 70) + w->year += 1900; + else + w->year += 2000; + } + if(w->mday == -1) + dignext = DATE_MDAY; + } + + if(!found) + return PARSEDATE_FAIL; + + date = p; + } + *datep = date; + *dignextp = dignext; + return PARSEDATE_OK; +} + +static int datecheck(struct when *w) +{ + if(w->sec == -1) + w->sec = w->min = w->hour = 0; /* no time, make it zero */ + + if((w->mday == -1) || (w->mon == -1) || (w->year == -1)) + /* lacks vital info, fail */ + return PARSEDATE_FAIL; + + /* The Gregorian calendar was introduced 1582 */ + else if(w->year < 1583) + return PARSEDATE_FAIL; + + else if((w->mday > 31) || (w->mon > 11) || (w->hour > 23) || + (w->min > 59) || (w->sec > 60)) + return PARSEDATE_FAIL; /* clearly an illegal date */ + + return PARSEDATE_OK; +} + +static void tzadjust(curl_off_t *tp, struct when *w) +{ + if(w->tzoff == -1) /* unknown tz means no offset */ + w->tzoff = 0; + + /* Add the time zone diff between local time zone and GMT. */ + if((w->tzoff > 0) && (*tp > (curl_off_t)(CURL_OFF_T_MAX - w->tzoff))) + *tp = CURL_OFF_T_MAX; + else + *tp += w->tzoff; + /* this needs no minimum check since we require a year > 1582 */ +} + +static int mktimet(curl_off_t seconds, time_t *output) +{ +#if SIZEOF_TIME_T < 5 + if(seconds > TIME_T_MAX) { + *output = TIME_T_MAX; + return PARSEDATE_LATER; + } + else if(seconds < TIME_T_MIN) { + *output = TIME_T_MIN; + return PARSEDATE_SOONER; + } +#elif defined(HAVE_TIME_T_UNSIGNED) + if(seconds < 0) { + *output = 0; + return PARSEDATE_SOONER; + } +#endif + *output = (time_t)seconds; + return PARSEDATE_OK; +} + static int parsedate(const char *date, time_t *output) { - time_t t = 0; - int wdaynum = -1; /* day of the week number, 0-6 (mon-sun) */ - int monnum = -1; /* month of the year number, 0-11 */ - int mdaynum = -1; /* day of month, 1 - 31 */ - int hournum = -1; - int minnum = -1; - int secnum = -1; - int yearnum = -1; - int tzoff = -1; + curl_off_t seconds = 0; enum assume dignext = DATE_MDAY; const char *indate = date; /* save the original pointer */ int part = 0; /* max 6 parts */ + int rc = 0; + struct when w; + initwhen(&w); while(*date && (part < 6)) { - bool found = FALSE; - skip(&date); - if(ISALPHA(*date)) { - /* a name coming up */ - size_t len = 0; - const char *p = date; - while(ISALPHA(*p) && (len < NAME_LEN)) { - p++; - len++; - } - - if(len != NAME_LEN) { - if(wdaynum == -1) { - wdaynum = checkday(date, len); - if(wdaynum != -1) - found = TRUE; - } - if(!found && (monnum == -1)) { - monnum = checkmonth(date, len); - if(monnum != -1) - found = TRUE; - } - - if(!found && (tzoff == -1)) { - /* this must be a time zone string */ - tzoff = checktz(date, len); - if(tzoff != -1) - found = TRUE; - } - } - if(!found) - return PARSEDATE_FAIL; /* bad string */ - - date += len; - } - else if(ISDIGIT(*date)) { - /* a digit */ - unsigned int val; - char *end; - if((secnum == -1) && - match_time(date, &hournum, &minnum, &secnum, &end)) { - /* time stamp */ - date = end; - } - else { - curl_off_t lval; - int num_digits = 0; - const char *p = date; - if(curlx_str_number(&p, &lval, 99999999)) - return PARSEDATE_FAIL; - - /* we know num_digits cannot be larger than 8 */ - num_digits = (int)(p - date); - val = (unsigned int)lval; - - if((tzoff == -1) && - (num_digits == 4) && - (val <= 1400) && - (indate < date) && - (date[-1] == '+' || date[-1] == '-')) { - /* four digits and a value less than or equal to 1400 (to take into - account all sorts of funny time zone diffs) and it is preceded - with a plus or minus. This is a time zone indication. 1400 is - picked since +1300 is frequently used and +1400 is mentioned as - an edge number in the document "ISO C 200X Proposal: Timezone - Functions" at http://david.tribble.com/text/c0xtimezone.html If - anyone has a more authoritative source for the exact maximum time - zone offsets, please speak up! */ - found = TRUE; - tzoff = ((val / 100 * 60) + (val % 100)) * 60; - - /* the + and - prefix indicates the local time compared to GMT, - this we need their reversed math to get what we want */ - tzoff = date[-1] == '+' ? -tzoff : tzoff; - } - - else if((num_digits == 8) && - (yearnum == -1) && - (monnum == -1) && - (mdaynum == -1)) { - /* 8 digits, no year, month or day yet. This is YYYYMMDD */ - found = TRUE; - yearnum = val / 10000; - monnum = ((val % 10000) / 100) - 1; /* month is 0 - 11 */ - mdaynum = val % 100; - } - - if(!found && (dignext == DATE_MDAY) && (mdaynum == -1)) { - if((val > 0) && (val < 32)) { - mdaynum = val; - found = TRUE; - } - dignext = DATE_YEAR; - } - - if(!found && (dignext == DATE_YEAR) && (yearnum == -1)) { - yearnum = val; - found = TRUE; - if(yearnum < 100) { - if(yearnum > 70) - yearnum += 1900; - else - yearnum += 2000; - } - if(mdaynum == -1) - dignext = DATE_MDAY; - } - - if(!found) - return PARSEDATE_FAIL; - - date = p; - } - } + if(ISALPHA(*date)) + rc = datestring(&date, &w); + else if(ISDIGIT(*date)) + rc = datenum(indate, &date, &w, &dignext); + if(rc) + return rc; part++; } - if(secnum == -1) - secnum = minnum = hournum = 0; /* no time, make it zero */ + rc = datecheck(&w); + if(rc) + return rc; - if((mdaynum == -1) || - (monnum == -1) || - (yearnum == -1)) - /* lacks vital info, fail */ - return PARSEDATE_FAIL; + seconds = time2epoch(&w); /* get number of seconds */ + tzadjust(&seconds, &w); /* handle the time zone offset */ + rc = mktimet(seconds, output); /* squeeze seconds into a time_t */ -#ifdef HAVE_TIME_T_UNSIGNED - if(yearnum < 1970) { - /* only positive numbers cannot return earlier */ - *output = TIME_T_MIN; - return PARSEDATE_SOONER; - } -#endif - -#if (SIZEOF_TIME_T < 5) - -#ifdef HAVE_TIME_T_UNSIGNED - /* an unsigned 32-bit time_t can only hold dates to 2106 */ - if(yearnum > 2105) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; - } -#else - /* a signed 32-bit time_t can only hold dates to the beginning of 2038 */ - if(yearnum > 2037) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; - } - if(yearnum < 1903) { - *output = TIME_T_MIN; - return PARSEDATE_SOONER; - } -#endif - -#else - /* The Gregorian calendar was introduced 1582 */ - if(yearnum < 1583) - return PARSEDATE_FAIL; -#endif - - if((mdaynum > 31) || (monnum > 11) || - (hournum > 23) || (minnum > 59) || (secnum > 60)) - return PARSEDATE_FAIL; /* clearly an illegal date */ - - /* time2epoch() returns a time_t. time_t is often 32 bits, sometimes even on - architectures that feature a 64 bits 'long' but ultimately time_t is the - correct data type to use. - */ - t = time2epoch(secnum, minnum, hournum, mdaynum, monnum, yearnum); - - /* Add the time zone diff between local time zone and GMT. */ - if(tzoff == -1) - tzoff = 0; - - if((tzoff > 0) && (t > (time_t)(TIME_T_MAX - tzoff))) { - *output = TIME_T_MAX; - return PARSEDATE_LATER; /* time_t overflow */ - } - - t += tzoff; - - *output = t; - - return PARSEDATE_OK; + return rc; } #else /* disabled */
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c index 4ede0a5..44e4244 100644 --- a/Utilities/cmcurl/lib/pingpong.c +++ b/Utilities/cmcurl/lib/pingpong.c
@@ -39,29 +39,21 @@ #include "select.h" #include "progress.h" -/* Returns timeout in ms. 0 or negative number means the timeout has already - triggered */ -timediff_t Curl_pp_state_timeout(struct Curl_easy *data, - struct pingpong *pp) +timediff_t Curl_pp_state_timeleft_ms(struct Curl_easy *data, + struct pingpong *pp) { - timediff_t timeout_ms, xfer_timeout_ms; - timediff_t response_time = data->set.server_response_timeout ? - data->set.server_response_timeout : RESP_TIMEOUT; + timediff_t xfer_remain_ms; + timediff_t remain_ms = data->set.server_response_timeout ? + data->set.server_response_timeout : PINGPONG_TIMEOUT_MS; - /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine - remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is - supposed to govern the response for any given server response, not for - the time from connect to the given server response. */ - - /* Without a requested timeout, we only wait 'response_time' seconds for the - full response to arrive before we bail out */ - timeout_ms = response_time - - curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response); - /* transfer timeout can be 0, which means no timeout applies */ - xfer_timeout_ms = Curl_timeleft_ms(data); - if(xfer_timeout_ms && (xfer_timeout_ms < timeout_ms)) - return xfer_timeout_ms; - return timeout_ms; + /* If the overall transfer has less time remaining than pingpong + * has otherwise for the state, return that. */ + remain_ms -= curlx_ptimediff_ms(Curl_pgrs_now(data), &pp->response); + /* transfer remaining time is 0, when it has no timeout. */ + xfer_remain_ms = Curl_timeleft_ms(data); + if(xfer_remain_ms) + return CURLMIN(remain_ms, xfer_remain_ms); + return remain_ms; } /* @@ -75,7 +67,7 @@ curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc; timediff_t interval_ms; - timediff_t timeout_ms = Curl_pp_state_timeout(data, pp); + timediff_t timeout_ms = Curl_pp_state_timeleft_ms(data, pp); CURLcode result = CURLE_OK; if(timeout_ms <= 0) {
diff --git a/Utilities/cmcurl/lib/pingpong.h b/Utilities/cmcurl/lib/pingpong.h index 33d2a92..864f2c6 100644 --- a/Utilities/cmcurl/lib/pingpong.h +++ b/Utilities/cmcurl/lib/pingpong.h
@@ -70,6 +70,10 @@ read */ }; +/* Default pingpong response timeout in milliseconds, unless a transfer + * has CURLOPT_SERVER_RESPONSE_TIMEOUT(_MS) set. */ +#define PINGPONG_TIMEOUT_MS (60 * 1000) + #define PINGPONG_SETUP(pp, s, e) \ do { \ (pp)->statemachine = s; \ @@ -88,10 +92,10 @@ /* initialize stuff to prepare for reading a fresh new response */ void Curl_pp_init(struct pingpong *pp, const struct curltime *pnow); -/* Returns timeout in ms. 0 or negative number means the timeout has already - triggered */ -timediff_t Curl_pp_state_timeout(struct Curl_easy *data, - struct pingpong *pp); +/* Returns time remaining in ms. 0 or negative number means the + timeout has already triggered */ +timediff_t Curl_pp_state_timeleft_ms(struct Curl_easy *data, + struct pingpong *pp); /*********************************************************************** *
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c index de704d1..317c04b 100644 --- a/Utilities/cmcurl/lib/pop3.c +++ b/Utilities/cmcurl/lib/pop3.c
@@ -1075,7 +1075,6 @@ { /* This code could be made into a special function in the handler struct */ CURLcode result = CURLE_OK; - struct SingleRequest *k = &data->req; struct connectdata *conn = data->conn; struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN); bool strip_dot = FALSE; @@ -1185,7 +1184,7 @@ message as per RFC-1939, sect. 3 */ result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2); - k->keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); pop3c->eob = 0; return result; @@ -1230,7 +1229,7 @@ when there is no body to return. */ pop3c->eob = 2; - /* But since this initial CR LF pair is not part of the actual body, we set + /* Since this initial CR LF pair is not part of the actual body, we set the strip counter here so that these bytes will not be delivered. */ pop3c->strip = 2; @@ -1480,8 +1479,8 @@ } /* Cleanup our per-request based variables */ - Curl_safefree(pop3->id); - Curl_safefree(pop3->custom); + curlx_safefree(pop3->id); + curlx_safefree(pop3->custom); /* Clear the transfer mode for the next request */ pop3->transfer = PPTRANSFER_BODY; @@ -1628,7 +1627,7 @@ Curl_pp_disconnect(&pop3c->pp); /* Cleanup our connection based variables */ - Curl_safefree(pop3c->apoptimestamp); + curlx_safefree(pop3c->apoptimestamp); return CURLE_OK; } @@ -1656,8 +1655,8 @@ (void)klen; DEBUGASSERT(pop3); /* Cleanup our per-request based variables */ - Curl_safefree(pop3->id); - Curl_safefree(pop3->custom); + curlx_safefree(pop3->id); + curlx_safefree(pop3->custom); curlx_free(pop3); } @@ -1668,7 +1667,7 @@ (void)klen; DEBUGASSERT(pop3c); Curl_pp_disconnect(&pop3c->pp); - Curl_safefree(pop3c->apoptimestamp); + curlx_safefree(pop3c->apoptimestamp); curlx_free(pop3c); } @@ -1692,7 +1691,7 @@ /* * POP3 protocol. */ -static const struct Curl_protocol Curl_protocol_pop3 = { +const struct Curl_protocol Curl_protocol_pop3 = { pop3_setup_connection, /* setup_connection */ pop3_do, /* do_it */ pop3_done, /* done */ @@ -1707,43 +1706,9 @@ pop3_disconnect, /* disconnect */ pop3_write, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif /* CURL_DISABLE_POP3 */ - -/* - * POP3 protocol handler. - */ -const struct Curl_scheme Curl_scheme_pop3 = { - "pop3", /* scheme */ -#ifdef CURL_DISABLE_POP3 - ZERO_NULL, -#else - &Curl_protocol_pop3, -#endif - CURLPROTO_POP3, /* protocol */ - CURLPROTO_POP3, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE, - PORT_POP3, /* defport */ -}; - -/* - * POP3S protocol handler. - */ -const struct Curl_scheme Curl_scheme_pop3s = { - "pop3s", /* scheme */ -#if defined(CURL_DISABLE_POP3) || !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_pop3, -#endif - CURLPROTO_POP3S, /* protocol */ - CURLPROTO_POP3, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, - PORT_POP3S, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/pop3.h b/Utilities/cmcurl/lib/pop3.h index 77e30f8..75c3a67 100644 --- a/Utilities/cmcurl/lib/pop3.h +++ b/Utilities/cmcurl/lib/pop3.h
@@ -23,7 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_pop3; -extern const struct Curl_scheme Curl_scheme_pop3s; +#ifndef CURL_DISABLE_POP3 +extern const struct Curl_protocol Curl_protocol_pop3; +#endif #endif /* HEADER_CURL_POP3_H */
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c index 6cde567..919c151 100644 --- a/Utilities/cmcurl/lib/progress.c +++ b/Utilities/cmcurl/lib/progress.c
@@ -33,7 +33,7 @@ #ifndef CURL_DISABLE_PROGRESS_METER /* Provide a string that is 7 letters long (plus the zero byte). - Unit test 1636. + @unittest 1636 */ UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds); UNITTEST void time2str(char *r, size_t rsize, curl_off_t seconds) @@ -83,7 +83,7 @@ but never longer than 6 columns (+ one zero byte). Add suffix k, M, G when suitable... - Unit test 1636 + @unittest 1636 */ UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen); UNITTEST char *max6out(curl_off_t bytes, char *max6, size_t mlen) @@ -127,9 +127,11 @@ } /* - * @unittest: 1606 + * @unittest 1606 */ UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, + const struct curltime *pnow); +UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, const struct curltime *pnow) { if(!data->set.low_speed_time || !data->set.low_speed_limit || @@ -211,6 +213,7 @@ Curl_pgrsSetUploadSize(data, -1); Curl_pgrsSetDownloadSize(data, -1); data->progress.speeder_c = 0; /* reset speed records */ + data->progress.deliver = 0; pgrs_speedinit(data); } @@ -339,6 +342,25 @@ p->ul_size_known = FALSE; } +/* check that the 'delta' amount of bytes are okay to deliver to the + application, or return error if not. */ +CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta) +{ + if(data->set.max_filesize && + ((curl_off_t)delta > data->set.max_filesize - data->progress.deliver)) { + failf(data, "Would have exceeded max file size"); + return CURLE_FILESIZE_EXCEEDED; + } + return CURLE_OK; +} + +/* this counts how much data is delivered to the application, which + in compressed cases may differ from downloaded amount */ +void Curl_pgrs_deliver_inc(struct Curl_easy *data, size_t delta) +{ + data->progress.deliver += delta; +} + void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta) { if(delta) { @@ -609,18 +631,18 @@ static CURLcode pgrsupdate(struct Curl_easy *data, bool showprogress) { if(!data->progress.hide) { + int rc; if(data->set.fxferinfo) { - int result; /* There is a callback set, call that */ Curl_set_in_callback(data, TRUE); - result = data->set.fxferinfo(data->set.progress_client, - data->progress.dl.total_size, - data->progress.dl.cur_size, - data->progress.ul.total_size, - data->progress.ul.cur_size); + rc = data->set.fxferinfo(data->set.progress_client, + data->progress.dl.total_size, + data->progress.dl.cur_size, + data->progress.ul.total_size, + data->progress.ul.cur_size); Curl_set_in_callback(data, FALSE); - if(result != CURL_PROGRESSFUNC_CONTINUE) { - if(result) { + if(rc != CURL_PROGRESSFUNC_CONTINUE) { + if(rc) { failf(data, "Callback aborted"); return CURLE_ABORTED_BY_CALLBACK; } @@ -628,17 +650,16 @@ } } else if(data->set.fprogress) { - int result; /* The older deprecated callback is set, call that */ Curl_set_in_callback(data, TRUE); - result = data->set.fprogress(data->set.progress_client, - (double)data->progress.dl.total_size, - (double)data->progress.dl.cur_size, - (double)data->progress.ul.total_size, - (double)data->progress.ul.cur_size); + rc = data->set.fprogress(data->set.progress_client, + (double)data->progress.dl.total_size, + (double)data->progress.dl.cur_size, + (double)data->progress.ul.total_size, + (double)data->progress.ul.cur_size); Curl_set_in_callback(data, FALSE); - if(result != CURL_PROGRESSFUNC_CONTINUE) { - if(result) { + if(rc != CURL_PROGRESSFUNC_CONTINUE) { + if(rc) { failf(data, "Callback aborted"); return CURLE_ABORTED_BY_CALLBACK; }
diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h index 9cb0924..7d419ec 100644 --- a/Utilities/cmcurl/lib/progress.h +++ b/Utilities/cmcurl/lib/progress.h
@@ -50,7 +50,8 @@ void Curl_pgrsStartNow(struct Curl_easy *data); void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size); void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size); - +CURLcode Curl_pgrs_deliver_check(struct Curl_easy *data, size_t delta); +void Curl_pgrs_deliver_inc(struct Curl_easy *data, size_t delta); void Curl_pgrs_download_inc(struct Curl_easy *data, size_t delta); void Curl_pgrs_upload_inc(struct Curl_easy *data, size_t delta); void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size); @@ -82,9 +83,4 @@ void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent); -#ifdef UNITTESTS -UNITTEST CURLcode pgrs_speedcheck(struct Curl_easy *data, - const struct curltime *pnow); -#endif - #endif /* HEADER_CURL_PROGRESS_H */
diff --git a/Utilities/cmcurl/lib/protocol.c b/Utilities/cmcurl/lib/protocol.c new file mode 100644 index 0000000..c8d4325 --- /dev/null +++ b/Utilities/cmcurl/lib/protocol.c
@@ -0,0 +1,485 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#include "protocol.h" +#include "strcase.h" + +#include "dict.h" +#include "file.h" +#include "ftp.h" +#include "gopher.h" +#include "http.h" +#include "imap.h" +#include "curl_ldap.h" +#include "mqtt.h" +#include "pop3.h" +#include "rtsp.h" +#include "smb.h" +#include "smtp.h" +#include "telnet.h" +#include "tftp.h" +#include "ws.h" +#include "vssh/ssh.h" + + +/* All URI schemes known to libcurl, but not necessarily implemented + * by protocol handlers. */ +const struct Curl_scheme Curl_scheme_dict = { + "dict", /* scheme */ +#ifdef CURL_DISABLE_DICT + ZERO_NULL, +#else + &Curl_protocol_dict, +#endif + CURLPROTO_DICT, /* protocol */ + CURLPROTO_DICT, /* family */ + PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */ + PORT_DICT, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_file = { + "file", /* scheme */ +#ifdef CURL_DISABLE_FILE + ZERO_NULL, +#else + &Curl_protocol_file, +#endif + CURLPROTO_FILE, /* protocol */ + CURLPROTO_FILE, /* family */ + PROTOPT_NONETWORK | PROTOPT_NOURLQUERY, /* flags */ + 0 /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ftp = { + "ftp", /* scheme */ +#ifdef CURL_DISABLE_FTP + ZERO_NULL, +#else + &Curl_protocol_ftp, +#endif + CURLPROTO_FTP, /* protocol */ + CURLPROTO_FTP, /* family */ + PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD | + PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP | + PROTOPT_WILDCARD | PROTOPT_SSL_REUSE | + PROTOPT_CONN_REUSE, /* flags */ + PORT_FTP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ftps = { + "ftps", /* scheme */ +#if defined(CURL_DISABLE_FTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_ftp, +#endif + CURLPROTO_FTPS, /* protocol */ + CURLPROTO_FTP, /* family */ + PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION | + PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD | + PROTOPT_CONN_REUSE, /* flags */ + PORT_FTPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_gopher = { + "gopher", /* scheme */ +#ifdef CURL_DISABLE_GOPHER + ZERO_NULL, +#else + &Curl_protocol_gopher, +#endif + CURLPROTO_GOPHER, /* protocol */ + CURLPROTO_GOPHER, /* family */ + PROTOPT_NONE, /* flags */ + PORT_GOPHER, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_gophers = { + "gophers", /* scheme */ +#if defined(CURL_DISABLE_GOPHER) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_gophers, +#endif + CURLPROTO_GOPHERS, /* protocol */ + CURLPROTO_GOPHER, /* family */ + PROTOPT_SSL, /* flags */ + PORT_GOPHER, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_http = { + "http", /* scheme */ +#ifdef CURL_DISABLE_HTTP + ZERO_NULL, +#else + &Curl_protocol_http, +#endif + CURLPROTO_HTTP, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PORT_HTTP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_https = { + "https", /* scheme */ +#if defined(CURL_DISABLE_HTTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_http, +#endif + CURLPROTO_HTTPS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */ + PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE, + PORT_HTTPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_imap = { + "imap", /* scheme */ +#ifdef CURL_DISABLE_IMAP + ZERO_NULL, +#else + &Curl_protocol_imap, +#endif + CURLPROTO_IMAP, /* protocol */ + CURLPROTO_IMAP, /* family */ + PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | + PROTOPT_CONN_REUSE, + PORT_IMAP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_imaps = { + "imaps", /* scheme */ +#if defined(CURL_DISABLE_IMAP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_imap, +#endif + CURLPROTO_IMAPS, /* protocol */ + CURLPROTO_IMAP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_IMAPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ldap = { + "ldap", /* scheme */ +#ifdef CURL_DISABLE_LDAP + ZERO_NULL, +#else + &Curl_protocol_ldap, +#endif + CURLPROTO_LDAP, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_SSL_REUSE, /* flags */ + PORT_LDAP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ldaps = { + "ldaps", /* scheme */ +#if defined(CURL_DISABLE_LDAP) || !defined(HAVE_LDAP_SSL) + ZERO_NULL, +#else + &Curl_protocol_ldap, +#endif + CURLPROTO_LDAPS, /* protocol */ + CURLPROTO_LDAP, /* family */ + PROTOPT_SSL, /* flags */ + PORT_LDAPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_mqtt = { + "mqtt", /* scheme */ +#ifdef CURL_DISABLE_MQTT + ZERO_NULL, +#else + &Curl_protocol_mqtt, +#endif + CURLPROTO_MQTT, /* protocol */ + CURLPROTO_MQTT, /* family */ + PROTOPT_NONE, /* flags */ + PORT_MQTT, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_mqtts = { + "mqtts", /* scheme */ +#if defined(CURL_DISABLE_MQTT) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_mqtts, +#endif + CURLPROTO_MQTTS, /* protocol */ + CURLPROTO_MQTT, /* family */ + PROTOPT_SSL, /* flags */ + PORT_MQTTS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_pop3 = { + "pop3", /* scheme */ +#ifdef CURL_DISABLE_POP3 + ZERO_NULL, +#else + &Curl_protocol_pop3, +#endif + CURLPROTO_POP3, /* protocol */ + CURLPROTO_POP3, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE, + PORT_POP3, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_pop3s = { + "pop3s", /* scheme */ +#if defined(CURL_DISABLE_POP3) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_pop3, +#endif + CURLPROTO_POP3S, /* protocol */ + CURLPROTO_POP3, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_POP3S, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_rtsp = { + "rtsp", /* scheme */ +#ifdef CURL_DISABLE_RTSP + ZERO_NULL, +#else + &Curl_protocol_rtsp, +#endif + CURLPROTO_RTSP, /* protocol */ + CURLPROTO_RTSP, /* family */ + PROTOPT_CONN_REUSE, /* flags */ + PORT_RTSP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_sftp = { + "sftp", /* scheme */ +#ifndef USE_SSH + NULL, +#else + &Curl_protocol_sftp, +#endif + CURLPROTO_SFTP, /* protocol */ + CURLPROTO_SFTP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE, + PORT_SSH /* defport */ +}; + +const struct Curl_scheme Curl_scheme_scp = { + "scp", /* scheme */ +#ifndef USE_SSH + NULL, +#else + &Curl_protocol_scp, +#endif + CURLPROTO_SCP, /* protocol */ + CURLPROTO_SCP, /* family */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE, + PORT_SSH, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smb = { + "smb", /* scheme */ +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) + &Curl_protocol_smb, +#else + ZERO_NULL, +#endif + CURLPROTO_SMB, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_NONE, /* flags */ + PORT_SMB, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smbs = { + "smbs", /* scheme */ +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) && defined(USE_SSL) + &Curl_protocol_smb, +#else + ZERO_NULL, +#endif + CURLPROTO_SMBS, /* protocol */ + CURLPROTO_SMB, /* family */ + PROTOPT_SSL, /* flags */ + PORT_SMBS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smtp = { + "smtp", /* scheme */ +#ifdef CURL_DISABLE_SMTP + ZERO_NULL, +#else + &Curl_protocol_smtp, +#endif + CURLPROTO_SMTP, /* protocol */ + CURLPROTO_SMTP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ + PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE, + PORT_SMTP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_smtps = { + "smtps", /* scheme */ +#if defined(CURL_DISABLE_SMTP) || !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_smtp, +#endif + CURLPROTO_SMTPS, /* protocol */ + CURLPROTO_SMTP, /* family */ + PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ + PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, + PORT_SMTPS, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_telnet = { + "telnet", /* scheme */ +#ifdef CURL_DISABLE_TELNET + ZERO_NULL, +#else + &Curl_protocol_telnet, +#endif + CURLPROTO_TELNET, /* protocol */ + CURLPROTO_TELNET, /* family */ + PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */ + PORT_TELNET, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_tftp = { + "tftp", /* scheme */ +#ifdef CURL_DISABLE_TFTP + ZERO_NULL, +#else + &Curl_protocol_tftp, +#endif + CURLPROTO_TFTP, /* protocol */ + CURLPROTO_TFTP, /* family */ + PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY, /* flags */ + PORT_TFTP, /* defport */ +}; + +const struct Curl_scheme Curl_scheme_ws = { + "ws", /* scheme */ +#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP) + ZERO_NULL, +#else + &Curl_protocol_ws, +#endif + CURLPROTO_WS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL, + PORT_HTTP /* defport */ +}; + +const struct Curl_scheme Curl_scheme_wss = { + "wss", /* scheme */ +#if defined(CURL_DISABLE_WEBSOCKETS) || defined(CURL_DISABLE_HTTP) || \ + !defined(USE_SSL) + ZERO_NULL, +#else + &Curl_protocol_ws, +#endif + CURLPROTO_WSS, /* protocol */ + CURLPROTO_HTTP, /* family */ + PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ + PROTOPT_USERPWDCTRL, + PORT_HTTPS /* defport */ +}; + +/* Returns a struct scheme pointer if the name is a known scheme. Check the + ->run struct field for non-NULL to figure out if an implementation is + present. */ +const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) +{ + /* table generated by schemetable.c: + 1. gcc schemetable.c && ./a.out + 2. check how small the table gets + 3. tweak the hash algorithm, then rerun from 1 + 4. when the table is good enough + 5. copy the table into this source code + 6. make sure this function uses the same hash function that worked for + schemetable.c + */ + static const struct Curl_scheme * const all_schemes[47] = { + &Curl_scheme_mqtt, + &Curl_scheme_smtp, + &Curl_scheme_tftp, + &Curl_scheme_imap, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + &Curl_scheme_ldaps, + &Curl_scheme_dict, NULL, + &Curl_scheme_file, NULL, + &Curl_scheme_pop3s, + &Curl_scheme_ftp, + &Curl_scheme_scp, + &Curl_scheme_mqtts, + &Curl_scheme_imaps, + &Curl_scheme_ldap, + &Curl_scheme_http, + &Curl_scheme_smb, NULL, NULL, + &Curl_scheme_telnet, + &Curl_scheme_https, + &Curl_scheme_gopher, + &Curl_scheme_rtsp, NULL, NULL, + &Curl_scheme_wss, NULL, + &Curl_scheme_gophers, + &Curl_scheme_smtps, + &Curl_scheme_pop3, + &Curl_scheme_ws, NULL, NULL, + &Curl_scheme_sftp, + &Curl_scheme_ftps, NULL, + &Curl_scheme_smbs, NULL, + }; + + if(len && (len <= 7)) { + const char *s = scheme; + size_t l = len; + const struct Curl_scheme *h; + unsigned int c = 792; + while(l) { + c <<= 4; + c += (unsigned int)Curl_raw_tolower(*s); + s++; + l--; + } + + h = all_schemes[c % 47]; + if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) + return h; + } + return NULL; +} + +const struct Curl_scheme *Curl_get_scheme(const char *scheme) +{ + return Curl_getn_scheme(scheme, strlen(scheme)); +}
diff --git a/Utilities/cmcurl/lib/protocol.h b/Utilities/cmcurl/lib/protocol.h new file mode 100644 index 0000000..f825409 --- /dev/null +++ b/Utilities/cmcurl/lib/protocol.h
@@ -0,0 +1,276 @@ +#ifndef HEADER_CURL_PROTOCOL_H +#define HEADER_CURL_PROTOCOL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +/* This file is for lib internal stuff */ +#include "curl_setup.h" + +struct Curl_easy; +struct connectdata; +struct easy_pollset; + +/* Known protocol default port numbers */ +#define PORT_FTP 21 +#define PORT_FTPS 990 +#define PORT_TELNET 23 +#define PORT_HTTP 80 +#define PORT_HTTPS 443 +#define PORT_DICT 2628 +#define PORT_LDAP 389 +#define PORT_LDAPS 636 +#define PORT_TFTP 69 +#define PORT_SSH 22 +#define PORT_IMAP 143 +#define PORT_IMAPS 993 +#define PORT_POP3 110 +#define PORT_POP3S 995 +#define PORT_SMB 445 +#define PORT_SMBS 445 +#define PORT_SMTP 25 +#define PORT_SMTPS 465 /* sometimes called SSMTP */ +#define PORT_RTSP 554 +#define PORT_GOPHER 70 +#define PORT_MQTT 1883 +#define PORT_MQTTS 8883 + +/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, + * the rest are internal information. If we use higher bits we only do this on + * platforms that have a >= 64-bit type and then we use such a type for the + * protocol fields in the scheme definition. + */ +#define CURLPROTO_WS (1L << 30) +#define CURLPROTO_WSS ((curl_prot_t)1 << 31) +#define CURLPROTO_MQTTS (1LL << 32) + +#define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff) + +/* the default protocols accepting a redirect to */ +#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \ + CURLPROTO_FTPS) + +typedef curl_off_t curl_prot_t; + +/* This mask is for all the old protocols that are provided and defined in the + public header and shall exclude protocols added since which are not exposed + in the API */ +#define CURLPROTO_MASK 0x3fffffff + +/* Convenience defines for checking protocols or their SSL based version. Each + protocol scheme should only ever have a single CURLPROTO_ in its protocol + field. */ +#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_WS | \ + CURLPROTO_WSS) +#define PROTO_FAMILY_FTP (CURLPROTO_FTP | CURLPROTO_FTPS) +#define PROTO_FAMILY_POP3 (CURLPROTO_POP3 | CURLPROTO_POP3S) +#define PROTO_FAMILY_SMB (CURLPROTO_SMB | CURLPROTO_SMBS) +#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP | CURLPROTO_SMTPS) +#define PROTO_FAMILY_SSH (CURLPROTO_SCP | CURLPROTO_SFTP) + +#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ + !defined(CURL_DISABLE_POP3) +/* these protocols support CURLOPT_DIRLISTONLY */ +#define CURL_LIST_ONLY_PROTOCOL 1 +#endif + +/* When redirecting transfers. */ +typedef enum { + FOLLOW_NONE, /* not used within the function, a placeholder to allow + initing to this */ + FOLLOW_FAKE, /* only records stuff, not actually following */ + FOLLOW_RETRY, /* set if this is a request retry as opposed to a real + redirect following */ + FOLLOW_REDIR /* a full true redirect */ +} followtype; + +/* + * Specific protocol handler, an implementation of one or more URI schemes. + */ +struct Curl_protocol { + /* Complement to setup_connection_internals(). This is done before the + transfer "owns" the connection. */ + CURLcode (*setup_connection)(struct Curl_easy *data, + struct connectdata *conn); + + /* These two functions MUST be set to be protocol dependent */ + CURLcode (*do_it)(struct Curl_easy *data, bool *done); + CURLcode (*done)(struct Curl_easy *, CURLcode, bool); + + /* If the curl_do() function is better made in two halves, this + * curl_do_more() function will be called afterwards, if set. For example + * for doing the FTP stuff after the PASV/PORT command. + */ + CURLcode (*do_more)(struct Curl_easy *, int *); + + /* This function *MAY* be set to a protocol-dependent function that is run + * after the connect() and everything is done, as a step in the connection. + * The 'done' pointer points to a bool that should be set to TRUE if the + * function completes before return. If it does not complete, the caller + * should call the ->connecting() function until it is. + */ + CURLcode (*connect_it)(struct Curl_easy *data, bool *done); + + /* See above. */ + CURLcode (*connecting)(struct Curl_easy *data, bool *done); + CURLcode (*doing)(struct Curl_easy *data, bool *done); + + /* Called from the multi interface during the PROTOCONNECT phase, and it + should then return a proper fd set */ + CURLcode (*proto_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DOING phase, and it should + then return a proper fd set */ + CURLcode (*doing_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DO_MORE phase, and it should + then return a proper fd set */ + CURLcode (*domore_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* Called from the multi interface during the DO_DONE, PERFORM and + WAITPERFORM phases, and it should then return a proper fd set. Not setting + this will make libcurl use the generic default one. */ + CURLcode (*perform_pollset)(struct Curl_easy *data, + struct easy_pollset *ps); + + /* This function *MAY* be set to a protocol-dependent function that is run + * by the curl_disconnect(), as a step in the disconnection. If the handler + * is called because the connection has been considered dead, + * dead_connection is set to TRUE. The connection is (again) associated with + * the transfer here. + */ + CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, + bool dead_connection); + + /* If used, this function gets called from transfer.c to + allow the protocol to do extra handling in writing response to + the client. */ + CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen, + bool is_eos); + + /* If used, this function gets called from transfer.c to + allow the protocol to do extra handling in writing a single response + header line to the client. */ + CURLcode (*write_resp_hd)(struct Curl_easy *data, + const char *hd, size_t hdlen, bool is_eos); + + /* If used, this function checks for a connection managed by this + protocol and currently not in use, if it should be considered dead. */ + bool (*connection_is_dead)(struct Curl_easy *data, + struct connectdata *conn); + + /* attach() attaches this transfer to this connection */ + void (*attach)(struct Curl_easy *data, struct connectdata *conn); + + /* return CURLE_OK if a redirect to `newurl` should be followed, + CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change + the way the follow request is performed. */ + CURLcode (*follow)(struct Curl_easy *data, const char *newurl, + followtype type); +}; + +#define PROTOPT_NONE 0 /* nothing extra */ +#define PROTOPT_SSL (1 << 0) /* uses SSL */ +#define PROTOPT_DUAL (1 << 1) /* this protocol uses two connections */ +#define PROTOPT_CLOSEACTION (1 << 2) /* need action before socket close */ +/* some protocols will have to call the underlying functions without regard to + what exact state the socket signals. IE even if the socket says "readable", + the send function might need to be called while uploading, or vice versa. +*/ +#define PROTOPT_DIRLOCK (1 << 3) +#define PROTOPT_NONETWORK (1 << 4) /* protocol does not use the network! */ +#define PROTOPT_NEEDSPWD (1 << 5) /* needs a password, and if none is set it + gets a default */ +#define PROTOPT_NOURLQUERY (1 << 6) /* protocol cannot handle + URL query strings (?foo=bar) ! */ +#define PROTOPT_CREDSPERREQUEST (1 << 7) /* requires login credentials per + request instead of per + connection */ +#define PROTOPT_ALPN (1 << 8) /* set ALPN for this */ +/* (1 << 9) was PROTOPT_STREAM, now free */ +#define PROTOPT_URLOPTIONS (1 << 10) /* allow options part in the userinfo + field of the URL */ +#define PROTOPT_PROXY_AS_HTTP (1 << 11) /* allow this non-HTTP scheme over a + HTTP proxy as HTTP proxies may know + this protocol and act as + a gateway */ +#define PROTOPT_WILDCARD (1 << 12) /* protocol supports wildcard matching */ +#define PROTOPT_USERPWDCTRL (1 << 13) /* Allow "control bytes" (< 32 ASCII) in + username and password */ +#define PROTOPT_NOTCPPROXY (1 << 14) /* this protocol cannot proxy over TCP */ +#define PROTOPT_SSL_REUSE (1 << 15) /* this protocol may reuse an existing + SSL connection in the same family + without having PROTOPT_SSL. */ +#define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ + +/* Everything about a URI scheme. */ +struct Curl_scheme { + const char *name; /* URL scheme name in lowercase */ + const struct Curl_protocol *run; /* implementation, optional */ + curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single + specific protocol bit */ + curl_prot_t family; /* single bit for protocol family; the non-TLS name + of the protocol this is */ + uint32_t flags; /* Extra particular characteristics, see PROTOPT_* */ + uint16_t defport; /* Default port. */ +}; + +/* Get scheme definition for a URI scheme name + * @param scheme URI scheme name, case-insensitive + * @return NULL if scheme is not known + */ +const struct Curl_scheme *Curl_get_scheme(const char *scheme); +const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len); + +/* For direct access to a URI scheme */ +extern const struct Curl_scheme Curl_scheme_dict; +extern const struct Curl_scheme Curl_scheme_file; +extern const struct Curl_scheme Curl_scheme_ftp; +extern const struct Curl_scheme Curl_scheme_ftps; +extern const struct Curl_scheme Curl_scheme_gopher; +extern const struct Curl_scheme Curl_scheme_gophers; +extern const struct Curl_scheme Curl_scheme_http; +extern const struct Curl_scheme Curl_scheme_https; +extern const struct Curl_scheme Curl_scheme_imap; +extern const struct Curl_scheme Curl_scheme_imaps; +extern const struct Curl_scheme Curl_scheme_ldap; +extern const struct Curl_scheme Curl_scheme_ldaps; +extern const struct Curl_scheme Curl_scheme_mqtt; +extern const struct Curl_scheme Curl_scheme_mqtts; +extern const struct Curl_scheme Curl_scheme_pop3; +extern const struct Curl_scheme Curl_scheme_pop3s; +extern const struct Curl_scheme Curl_scheme_rtsp; +extern const struct Curl_scheme Curl_scheme_scp; +extern const struct Curl_scheme Curl_scheme_sftp; +extern const struct Curl_scheme Curl_scheme_smb; +extern const struct Curl_scheme Curl_scheme_smbs; +extern const struct Curl_scheme Curl_scheme_smtp; +extern const struct Curl_scheme Curl_scheme_smtps; +extern const struct Curl_scheme Curl_scheme_telnet; +extern const struct Curl_scheme Curl_scheme_tftp; +extern const struct Curl_scheme Curl_scheme_ws; +extern const struct Curl_scheme Curl_scheme_wss; + +#endif /* HEADER_CURL_PROTOCOL_H */
diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c index 4232e81..dd82750 100644 --- a/Utilities/cmcurl/lib/rand.c +++ b/Utilities/cmcurl/lib/rand.c
@@ -34,30 +34,20 @@ #include "escape.h" #ifdef _WIN32 - -#ifndef CURL_WINDOWS_UWP -# include <bcrypt.h> -# ifdef _MSC_VER -# pragma comment(lib, "bcrypt.lib") -# endif -# ifndef STATUS_SUCCESS -# define STATUS_SUCCESS ((NTSTATUS)0x00000000L) -# endif +#include <bcrypt.h> +#ifndef STATUS_SUCCESS +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #endif CURLcode Curl_win32_random(unsigned char *entropy, size_t length) { memset(entropy, 0, length); -#ifndef CURL_WINDOWS_UWP if(BCryptGenRandom(NULL, entropy, (ULONG)length, BCRYPT_USE_SYSTEM_PREFERRED_RNG) != STATUS_SUCCESS) return CURLE_FAILED_INIT; return CURLE_OK; -#else - return CURLE_NOT_BUILT_IN; -#endif } #endif
diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h index 8676580..afccd0a 100644 --- a/Utilities/cmcurl/lib/rand.h +++ b/Utilities/cmcurl/lib/rand.h
@@ -25,7 +25,7 @@ ***************************************************************************/ CURLcode Curl_rand_bytes(struct Curl_easy *data, #ifdef DEBUGBUILD - bool allow_env_override, + bool env_override, #endif unsigned char *rnd, size_t num);
diff --git a/Utilities/cmcurl/lib/ratelimit.c b/Utilities/cmcurl/lib/ratelimit.c index 2d88cdd..dc01375 100644 --- a/Utilities/cmcurl/lib/ratelimit.c +++ b/Utilities/cmcurl/lib/ratelimit.c
@@ -157,8 +157,8 @@ DEBUGASSERT(rate_per_sec >= 0); DEBUGASSERT(burst_per_sec >= rate_per_sec || !burst_per_sec); DEBUGASSERT(pts); - r->rate_per_step = rate_per_sec; - r->burst_per_step = burst_per_sec; + r->rate_per_step = r->rate_per_sec = rate_per_sec; + r->burst_per_step = r->burst_per_sec = burst_per_sec; r->step_us = CURL_US_PER_SEC; r->spare_us = 0; r->tokens = r->rate_per_step; @@ -169,8 +169,13 @@ void Curl_rlimit_start(struct Curl_rlimit *r, const struct curltime *pts, int64_t total_tokens) { - r->tokens = r->rate_per_step; + /* A start always resets the values to initial defaults, then + * fine tunes the intervals for the total_tokens expected. */ + r->rate_per_step = r->rate_per_sec; + r->burst_per_step = r->burst_per_sec; + r->step_us = CURL_US_PER_SEC; r->spare_us = 0; + r->tokens = r->rate_per_step; r->ts = *pts; rlimit_tune_steps(r, total_tokens); }
diff --git a/Utilities/cmcurl/lib/ratelimit.h b/Utilities/cmcurl/lib/ratelimit.h index 7563734..3c3e38b 100644 --- a/Utilities/cmcurl/lib/ratelimit.h +++ b/Utilities/cmcurl/lib/ratelimit.h
@@ -55,6 +55,8 @@ */ struct Curl_rlimit { + int64_t rate_per_sec; /* rate tokens generated per second */ + int64_t burst_per_sec; /* burst rate of tokens per second */ int64_t rate_per_step; /* rate tokens generated per step us */ int64_t burst_per_step; /* burst rate of tokens per step us */ timediff_t step_us; /* microseconds between token increases */
diff --git a/Utilities/cmcurl/lib/request.c b/Utilities/cmcurl/lib/request.c index 6607753..c414383 100644 --- a/Utilities/cmcurl/lib/request.c +++ b/Utilities/cmcurl/lib/request.c
@@ -65,6 +65,11 @@ req->httpversion = 0; req->sendbuf_hds_len = 0; + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif + result = Curl_client_start(data); if(result) return result; @@ -102,9 +107,6 @@ if(!aborted) (void)req_flush(data); Curl_client_reset(data); -#ifndef CURL_DISABLE_DOH - Curl_doh_close(data); -#endif return CURLE_OK; } @@ -112,14 +114,20 @@ { struct curltime t0 = { 0, 0 }; - Curl_safefree(req->newurl); + curlx_safefree(req->newurl); + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif +#ifndef CURL_DISABLE_COOKIES + curlx_safefree(req->cookiehost); +#endif Curl_client_reset(data); if(req->sendbuf_init) Curl_bufq_reset(&req->sendbuf); -#ifndef CURL_DISABLE_DOH - Curl_doh_close(data); -#endif + /* clear any resolve data */ + Curl_resolv_destroy_all(data); /* Can no longer memset() this struct as we need to keep some state */ req->size = -1; req->maxdownload = -1; @@ -132,7 +140,7 @@ req->headerline = 0; req->offset = 0; req->httpcode = 0; - req->keepon = 0; + req->io_flags = 0; req->upgr101 = UPGR101_NONE; req->sendbuf_hds_len = 0; req->timeofdoc = 0; @@ -153,6 +161,7 @@ req->ignorebody = FALSE; req->http_bodyless = FALSE; req->chunk = FALSE; + req->resp_trailer = FALSE; req->ignore_cl = FALSE; req->upload_chunky = FALSE; req->no_body = data->set.opt_no_body; @@ -165,7 +174,11 @@ void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data) { - Curl_safefree(req->newurl); + curlx_safefree(req->newurl); + curlx_safefree(req->userpwd); +#ifndef CURL_DISABLE_PROXY + curlx_safefree(req->proxyuserpwd); +#endif if(req->sendbuf_init) Curl_bufq_free(&req->sendbuf); Curl_client_cleanup(data); @@ -256,7 +269,7 @@ { DEBUGASSERT(!data->req.upload_done); data->req.upload_done = TRUE; - data->req.keepon &= ~KEEP_SEND; /* we are done sending */ + CURL_REQ_CLEAR_SEND(data); Curl_pgrsTime(data, TIMER_POSTRANSFER); Curl_creader_done(data, data->req.upload_aborted); @@ -419,22 +432,22 @@ bool Curl_req_want_send(struct Curl_easy *data) { /* Not done and upload not blocked and either one of - * - KEEP_SEND + * - REQ_IO_SEND * - request has buffered data to send * - connection has pending data to send */ return !data->req.done && !Curl_rlimit_is_blocked(&data->progress.ul.rlimit) && - ((data->req.keepon & KEEP_SEND) || + (CURL_REQ_WANT_SEND(data) || !Curl_req_sendbuf_empty(data) || Curl_xfer_needs_flush(data)); } bool Curl_req_want_recv(struct Curl_easy *data) { - /* Not done and download not blocked and KEEP_RECV */ + /* Not done and download not blocked and want RECV */ return !data->req.done && !Curl_rlimit_is_blocked(&data->progress.dl.rlimit) && - (data->req.keepon & KEEP_RECV); + CURL_REQ_WANT_RECV(data); } bool Curl_req_done_sending(struct Curl_easy *data) @@ -470,7 +483,7 @@ if(!data->req.upload_done) { Curl_bufq_reset(&data->req.sendbuf); data->req.upload_aborted = TRUE; - data->req.keepon &= ~KEEP_SEND; + CURL_REQ_CLEAR_SEND(data); return req_set_upload_done(data); } return CURLE_OK; @@ -482,8 +495,8 @@ * We might still be paused on receive client writes though, so * keep those bits around. */ CURLcode result = CURLE_OK; - if(data->req.keepon & KEEP_SEND) + if(CURL_REQ_WANT_SEND(data)) result = Curl_req_abort_sending(data); - data->req.keepon &= ~(KEEP_RECV | KEEP_SEND); + CURL_REQ_CLEAR_IO(data); return result; }
diff --git a/Utilities/cmcurl/lib/request.h b/Utilities/cmcurl/lib/request.h index 5332d48..6948d79 100644 --- a/Utilities/cmcurl/lib/request.h +++ b/Utilities/cmcurl/lib/request.h
@@ -31,6 +31,25 @@ /* forward declarations */ struct UserDefined; +/* Bits on the io_flags member of SingleRequest */ +#define REQ_IO_RECV (1 << 0) /* there is or may be data to read */ +#define REQ_IO_SEND (1 << 1) /* there is or may be data to write */ + +/* Low level request receive/send io_flags checks. */ +#define CURL_REQ_WANT_SEND(d) ((d)->req.io_flags & REQ_IO_SEND) +#define CURL_REQ_WANT_RECV(d) ((d)->req.io_flags & REQ_IO_RECV) +#define CURL_REQ_WANT_IO(d) \ + ((d)->req.io_flags & (REQ_IO_RECV | REQ_IO_SEND)) +/* Low level request receive/send io_flags manipulations. */ +#define CURL_REQ_SET_SEND(d) ((d)->req.io_flags |= REQ_IO_SEND) +#define CURL_REQ_SET_RECV(d) ((d)->req.io_flags |= REQ_IO_RECV) +#define CURL_REQ_CLEAR_SEND(d) \ + ((d)->req.io_flags &= (uint8_t)~REQ_IO_SEND) +#define CURL_REQ_CLEAR_RECV(d) \ + ((d)->req.io_flags &= (uint8_t)~REQ_IO_RECV) +#define CURL_REQ_CLEAR_IO(d) \ + ((d)->req.io_flags &= (uint8_t)~(REQ_IO_RECV | REQ_IO_SEND)) + enum expect100 { EXP100_SEND_DATA, /* enough waiting, send the body now */ EXP100_AWAITING_CONTINUE, /* waiting for the 100 Continue header */ @@ -59,6 +78,8 @@ -1 means unlimited */ curl_off_t bytecount; /* total number of bytes read */ curl_off_t writebytecount; /* number of bytes written */ + curl_off_t offset; /* possible resume offset read from the + Content-Range: header */ struct curltime start; /* transfer started at this time */ unsigned int headerbytecount; /* received server headers (not CONNECT @@ -72,11 +93,8 @@ in a CURLE_GOT_NOTHING error code */ int headerline; /* counts header lines to better track the first one */ - curl_off_t offset; /* possible resume offset read from the - Content-Range: header */ int httpcode; /* error code from the 'HTTP/1.? XXX' or 'RTSP/1.? XXX' line */ - int keepon; unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */ unsigned char httpversion; /* Version in response (09, 10, 11, etc.) */ enum upgrade101 upgr101; /* 101 upgrade state */ @@ -94,7 +112,15 @@ header data */ char *newurl; /* Set to the new URL to use when a redirect or a retry is wanted */ + uint8_t io_flags; /* REQ_IO_RECV | REQ_IO_SEND */ + char *userpwd; /* auth header */ +#ifndef CURL_DISABLE_PROXY + char *proxyuserpwd; /* proxy auth header */ +#endif +#ifndef CURL_DISABLE_COOKIES + char *cookiehost; +#endif #ifndef CURL_DISABLE_COOKIES unsigned char setcookies; #endif @@ -109,8 +135,8 @@ BIT(eos_sent); /* iff EOS has been sent to the server */ BIT(rewind_read); /* iff reader needs rewind at next start */ BIT(upload_done); /* set to TRUE when all request data has been sent */ - BIT(upload_aborted); /* set to TRUE when upload was aborted. Will also - * show `upload_done` as TRUE. */ + BIT(upload_aborted); /* set to TRUE when upload was aborted. Also + * shows `upload_done` as TRUE. */ BIT(ignorebody); /* we read a response-body but we ignore it! */ BIT(http_bodyless); /* HTTP response status code is between 100 and 199, 204 or 304 */ @@ -207,13 +233,13 @@ /** * Stop sending any more request data to the server. - * Will clear the send buffer and mark request sending as done. + * Clear the send buffer and mark request sending as done. */ CURLcode Curl_req_abort_sending(struct Curl_easy *data); /** * Stop sending and receiving any more request data. - * Will abort sending if not done. + * Abort sending if not done. */ CURLcode Curl_req_stop_send_recv(struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/rtsp.c b/Utilities/cmcurl/lib/rtsp.c index 11848c8..b08767f 100644 --- a/Utilities/cmcurl/lib/rtsp.c +++ b/Utilities/cmcurl/lib/rtsp.c
@@ -126,19 +126,13 @@ /* * Function to check on various aspects of a connection. */ -static uint32_t rtsp_conncheck(struct Curl_easy *data, - struct connectdata *conn, - uint32_t checks_to_perform) +static bool rtsp_conn_is_dead(struct Curl_easy *data, + struct connectdata *conn) { - unsigned int ret_val = CONNRESULT_NONE; - - if(checks_to_perform & CONNCHECK_ISDEAD) { - bool input_pending; - if(!Curl_conn_is_alive(data, conn, &input_pending)) - ret_val |= CONNRESULT_DEAD; - } - - return ret_val; + bool input_pending; + /* Contrary to default handling, this protocol allows pending + * input on an unused connection. */ + return !Curl_conn_is_alive(data, conn, &input_pending); } static CURLcode rtsp_connect(struct Curl_easy *data, bool *done) @@ -166,7 +160,7 @@ struct rtsp_conn *rtspc = Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN); struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY); - CURLcode httpStatus; + CURLcode result; if(!rtspc || !rtsp) return CURLE_FAILED_INIT; @@ -175,9 +169,9 @@ if(data->set.rtspreq == RTSPREQ_RECEIVE) premature = TRUE; - httpStatus = Curl_http_done(data, status, premature); + result = Curl_http_done(data, status, premature); - if(!status && !httpStatus) { + if(!status && !result) { /* Check the sequence numbers */ uint32_t CSeq_sent = rtsp->CSeq_sent; uint32_t CSeq_recv = rtsp->CSeq_recv; @@ -197,7 +191,7 @@ } } - return httpStatus; + return result; } static CURLcode rtsp_setup_body(struct Curl_easy *data, @@ -240,7 +234,7 @@ /* As stated in the http comments, it is probably not wise to * actually set a custom Content-Length in the headers */ if(!Curl_checkheaders(data, STRCONST("Content-Length"))) { - result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T"\r\n", + result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T "\r\n", req_clen); if(result) return result; @@ -439,13 +433,13 @@ } } - /* The User-Agent string might have been allocated in url.c already, because + /* The User-Agent string might have been allocated already, because it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ if(Curl_checkheaders(data, STRCONST("User-Agent")) && data->state.aptr.uagent) { - Curl_safefree(data->state.aptr.uagent); + curlx_safefree(data->state.aptr.uagent); } else if(!Curl_checkheaders(data, STRCONST("User-Agent")) && data->set.str[STRING_USERAGENT]) { @@ -459,12 +453,12 @@ goto out; #ifndef CURL_DISABLE_PROXY - p_proxyuserpwd = data->state.aptr.proxyuserpwd; + p_proxyuserpwd = data->req.proxyuserpwd; #endif - p_userpwd = data->state.aptr.userpwd; + p_userpwd = data->req.userpwd; /* Referrer */ - Curl_safefree(data->state.aptr.ref); + curlx_safefree(data->state.aptr.ref); if(Curl_bufref_ptr(&data->state.referer) && !Curl_checkheaders(data, STRCONST("Referer"))) data->state.aptr.ref = @@ -544,12 +538,6 @@ p_proxyuserpwd ? p_proxyuserpwd : "", p_userpwd ? p_userpwd : ""); - /* - * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM - * with basic and digest, it will be freed anyway by the next request - */ - Curl_safefree(data->state.aptr.userpwd); - if(result) goto out; @@ -686,7 +674,7 @@ /* This could be the next response, no consume and return */ if(*pconsumed) { DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, " - "skipping %zd bytes of junk", *pconsumed)); + "skipping %zu bytes of junk", *pconsumed)); } rtspc->state = RTP_PARSE_SKIP; rtspc->in_header = TRUE; @@ -1017,7 +1005,7 @@ * * Allow any non whitespace content, up to the field separator or end of * line. RFC 2326 is not 100% clear on the session ID and for example - * gstreamer does url-encoded session ID's not covered by the standard. + * gstreamer does URL-encoded session ID's not covered by the standard. */ end = start; while((*end > ' ') && (*end != ';')) @@ -1057,7 +1045,7 @@ /* * RTSP handler interface. */ -static const struct Curl_protocol Curl_protocol_rtsp = { +const struct Curl_protocol Curl_protocol_rtsp = { rtsp_setup_connection, /* setup_connection */ rtsp_do, /* do_it */ rtsp_done, /* done */ @@ -1072,25 +1060,9 @@ ZERO_NULL, /* disconnect */ rtsp_rtp_write_resp, /* write_resp */ rtsp_rtp_write_resp_hd, /* write_resp_hd */ - rtsp_conncheck, /* connection_check */ + rtsp_conn_is_dead, /* connection_is_dead */ ZERO_NULL, /* attach connection */ Curl_http_follow, /* follow */ }; #endif /* CURL_DISABLE_RTSP */ - -/* - * RTSP handler interface. - */ -const struct Curl_scheme Curl_scheme_rtsp = { - "rtsp", /* scheme */ -#ifdef CURL_DISABLE_RTSP - ZERO_NULL, -#else - &Curl_protocol_rtsp, -#endif - CURLPROTO_RTSP, /* protocol */ - CURLPROTO_RTSP, /* family */ - PROTOPT_CONN_REUSE, /* flags */ - PORT_RTSP, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/rtsp.h b/Utilities/cmcurl/lib/rtsp.h index 92b28e5..dd5df3f 100644 --- a/Utilities/cmcurl/lib/rtsp.h +++ b/Utilities/cmcurl/lib/rtsp.h
@@ -23,9 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_rtsp; #ifndef CURL_DISABLE_RTSP CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header); +extern const struct Curl_protocol Curl_protocol_rtsp; #else #define Curl_rtsp_parseheader(x, y) CURLE_NOT_BUILT_IN #endif
diff --git a/Utilities/cmcurl/lib/select.c b/Utilities/cmcurl/lib/select.c index 9a11924..888490f 100644 --- a/Utilities/cmcurl/lib/select.c +++ b/Utilities/cmcurl/lib/select.c
@@ -75,17 +75,17 @@ #ifdef USE_WINSOCK /* Winsock select() must not be called with an fd_set that contains zero - fd flags, or it will return WSAEINVAL. But, it also cannot be called - with no fd_sets at all! From the documentation: + fd flags, or it will return WSAEINVAL. It also cannot be called with + no fd_sets at all! From the documentation: - Any two of the parameters, readfds, writefds, or exceptfds, can be - given as null. At least one must be non-null, and any non-null - descriptor set must contain at least one handle to a socket. + Any two of the parameters, readfds, writefds, or exceptfds, can be + given as null. At least one must be non-null, and any non-null + descriptor set must contain at least one handle to a socket. - It is unclear why Winsock does not handle this for us instead of - calling this an error. Luckily, with Winsock, we can _also_ ask how - many bits are set on an fd_set. So, let's check it beforehand. - */ + It is unclear why Winsock does not handle this for us instead of + calling this an error. Luckily, with Winsock, we can _also_ ask how + many bits are set on an fd_set. Therefore, let's check it beforehand. + */ return select((int)maxfd + 1, fds_read && fds_read->fd_count ? fds_read : NULL, fds_write && fds_write->fd_count ? fds_write : NULL, @@ -274,7 +274,7 @@ if(ufds[i].fd == CURL_SOCKET_BAD) continue; VERIFY_SOCK(ufds[i].fd); - if(ufds[i].events & (POLLIN |POLLOUT |POLLPRI | + if(ufds[i].events & (POLLIN | POLLOUT | POLLPRI | POLLRDNORM | POLLWRNORM | POLLRDBAND)) { if(ufds[i].fd > maxfd) maxfd = ufds[i].fd; @@ -636,19 +636,25 @@ bool do_in, bool do_out) { return Curl_pollset_change(data, ps, sock, - (do_in ? CURL_POLL_IN : 0)| + (do_in ? CURL_POLL_IN : 0) | (do_out ? CURL_POLL_OUT : 0), - (!do_in ? CURL_POLL_IN : 0)| + (!do_in ? CURL_POLL_IN : 0) | (!do_out ? CURL_POLL_OUT : 0)); } +/* + * Return values: + * -1 = error + * 0 = timeout + * N = number of structures with non zero revent fields + */ int Curl_pollset_poll(struct Curl_easy *data, struct easy_pollset *ps, timediff_t timeout_ms) { struct pollfd *pfds; unsigned int i, npfds; - int result; + int rc; (void)data; DEBUGASSERT(data); @@ -677,9 +683,9 @@ } } - result = Curl_poll(pfds, npfds, timeout_ms); + rc = Curl_poll(pfds, npfds, timeout_ms); curlx_free(pfds); - return result; + return rc; } void Curl_pollset_check(struct Curl_easy *data,
diff --git a/Utilities/cmcurl/lib/select.h b/Utilities/cmcurl/lib/select.h index ffcd44c..87b6954 100644 --- a/Utilities/cmcurl/lib/select.h +++ b/Utilities/cmcurl/lib/select.h
@@ -35,9 +35,7 @@ * Definition of pollfd struct and constants for platforms lacking them. */ -#if !defined(HAVE_SYS_POLL_H) && \ - !defined(HAVE_POLL_H) && \ - !defined(POLLIN) +#if !defined(HAVE_SYS_POLL_H) && !defined(HAVE_POLL_H) && !defined(POLLIN) #define POLLIN 0x01 #define POLLPRI 0x02
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c index 92e77b4..7c977d3 100644 --- a/Utilities/cmcurl/lib/sendf.c +++ b/Utilities/cmcurl/lib/sendf.c
@@ -372,8 +372,8 @@ The bit pattern defines to what "streams" to write to. Body and/or header. The defines are in sendf.h of course. */ -CURLcode Curl_client_write(struct Curl_easy *data, - int type, const char *buf, size_t len) +CURLcode Curl_client_write(struct Curl_easy *data, int type, const char *buf, + size_t len) { CURLcode result; @@ -675,7 +675,7 @@ case 0: if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) { failf(data, "client read function EOF fail, " - "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read", + "only %" FMT_OFF_T "/%" FMT_OFF_T " of needed bytes read", ctx->read_len, ctx->total_len); result = CURLE_READ_ERROR; break; @@ -729,8 +729,8 @@ *peos = (bool)ctx->seen_eos; break; } - CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"FMT_OFF_T - ", read=%"FMT_OFF_T") -> %d, nread=%zu, eos=%d", + CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%" FMT_OFF_T + ", read=%" FMT_OFF_T ") -> %d, nread=%zu, eos=%d", blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); return result; @@ -1009,6 +1009,8 @@ if(!nread || !memchr(buf, '\n', nread)) { /* nothing to convert, return this right away */ + if(nread) + ctx->prev_cr = (buf[nread - 1] == '\r'); if(ctx->read_eos) ctx->eos = TRUE; *pnread = nread; @@ -1128,7 +1130,7 @@ struct cr_in_ctx *ctx; result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT); - if(result) + if(result || !r) goto out; ctx = r->ctx; ctx->total_len = len; @@ -1136,7 +1138,7 @@ cl_reset_reader(data); result = do_init_reader_stack(data, r); out: - CURL_TRC_READ(data, "add fread reader, len=%"FMT_OFF_T " -> %d", + CURL_TRC_READ(data, "add fread reader, len=%" FMT_OFF_T " -> %d", len, result); return result; }
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c index 84f3e02..b8a6327 100644 --- a/Utilities/cmcurl/lib/setopt.c +++ b/Utilities/cmcurl/lib/setopt.c
@@ -49,6 +49,7 @@ #include "curlx/strdup.h" #include "escape.h" #include "bufref.h" +#include "vauth/vauth.h" static CURLcode setopt_set_timeout_sec(timediff_t *ptimeout_ms, long secs) { @@ -83,7 +84,7 @@ /* Release the previous storage at `charp' and replace by a dynamic storage copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ - Curl_safefree(*charp); + curlx_safefree(*charp); if(s) { if(strlen(s) > CURL_MAX_INPUT_LENGTH) @@ -103,7 +104,7 @@ /* free the previous storage at `blobp' and replace by a dynamic storage copy of blob. If CURL_BLOB_COPY is set, the data is copied. */ - Curl_safefree(*blobp); + curlx_safefree(*blobp); if(blob) { struct curl_blob *nblob; @@ -137,8 +138,8 @@ DEBUGASSERT(userp); DEBUGASSERT(passwdp); - /* Parse the login details if specified. It not then we treat NULL as a hint - to clear the existing data */ + /* Parse the login details if specified. If not, then we treat NULL as a + hint to clear the existing data */ if(option) { size_t len = strlen(option); CURLcode result; @@ -488,7 +489,7 @@ case CURLOPT_UPLOAD: case CURLOPT_PUT: /* - * We want to sent data to the remote host. If this is HTTP, that equals + * We want to send data to the remote host. If this is HTTP, that equals * using the PUT request. */ if(enabled) { @@ -613,7 +614,7 @@ #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) case CURLOPT_SOCKS5_GSSAPI_NEC: /* - * Set flag for NEC SOCK5 support + * Set flag for NEC SOCKS5 support */ s->socks5_gssapi_nec = enabled; break; @@ -1011,7 +1012,7 @@ case CURLOPT_SSL_ENABLE_NPN: break; case CURLOPT_SSLENGINE_DEFAULT: - Curl_safefree(s->str[STRING_SSL_ENGINE]); + curlx_safefree(s->str[STRING_SSL_ENGINE]); result = Curl_ssl_set_engine_default(data); break; default: @@ -1243,7 +1244,7 @@ return CURLE_BAD_FUNCTION_ARGUMENT; if(s->postfieldsize < arg && s->postfields == s->str[STRING_COPYPOSTFIELDS]) { - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->postfields = NULL; } s->postfieldsize = arg; @@ -1439,7 +1440,7 @@ s->opt_no_body = FALSE; /* this is implied */ #ifndef CURL_DISABLE_FORM_API Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); data->state.mimepost = NULL; #endif } @@ -1455,6 +1456,14 @@ CURLcode result = CURLE_OK; struct UserDefined *s = &data->set; switch(option) { + case CURLOPT_CURLU: + /* + * pass CURLU to set URL + */ + Curl_bufref_free(&data->state.url); + curlx_safefree(s->str[STRING_SET_URL]); + s->uh = va_arg(param, CURLU *); + break; #ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_FORM_API case CURLOPT_HTTPPOST: @@ -1465,7 +1474,7 @@ s->method = HTTPREQ_POST_FORM; s->opt_no_body = FALSE; /* this is implied */ Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); data->state.mimepost = NULL; break; #endif /* !CURL_DISABLE_FORM_API */ @@ -1490,67 +1499,17 @@ case CURLOPT_SHARE: { struct Curl_share *set = va_arg(param, struct Curl_share *); - /* disconnect from old share, if any */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); + /* disconnect from old share, if any and possible */ + result = Curl_share_easy_unlink(data); + if(result) + return result; -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies == data->cookies) - data->cookies = NULL; -#endif - -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts == data->hsts) - data->hsts = NULL; -#endif -#ifdef USE_LIBPSL - if(data->psl == &data->share->psl) - data->psl = data->multi ? &data->multi->psl : NULL; -#endif - if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) { - Curl_resolv_unlink(data, &data->state.dns[0]); - Curl_resolv_unlink(data, &data->state.dns[1]); - } - - data->share->dirty--; - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - data->share = NULL; + /* use new share if it set */ + if(GOOD_SHARE_HANDLE(set)) { + result = Curl_share_easy_link(data, set); + if(result) + return result; } - - if(GOOD_SHARE_HANDLE(set)) - /* use new share if it set */ - data->share = set; - if(data->share) { - - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - data->share->dirty++; - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies) { - /* use shared cookie list, first free own one if any */ - Curl_cookie_cleanup(data->cookies); - /* enable cookies since we now use a share that uses cookies! */ - data->cookies = data->share->cookies; - } -#endif /* CURL_DISABLE_HTTP */ -#ifndef CURL_DISABLE_HSTS - if(data->share->hsts) { - /* first free the private one if any */ - Curl_hsts_cleanup(&data->hsts); - data->hsts = data->share->hsts; - } -#endif -#ifdef USE_LIBPSL - if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL)) - data->psl = &data->share->psl; -#endif - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } - /* check for host cache not needed, - * it will be done by curl_easy_perform */ break; } @@ -1664,6 +1623,18 @@ #endif #ifndef CURL_DISABLE_PROXY +static CURLcode setproxy(struct Curl_easy *data, const char *proxy) +{ + if((data->set.str[STRING_PROXY] && proxy) && + /* there was one set, is this a new one? */ + !strcmp(data->set.str[STRING_PROXY], proxy)) + return CURLE_OK; /* same one as before */ + + Curl_auth_digest_cleanup(&data->state.proxydigest); + memset(&data->state.authproxy, 0, sizeof(data->state.authproxy)); + return Curl_setstropt(&data->set.str[STRING_PROXY], proxy); +} + static CURLcode setopt_cptr_proxy(struct Curl_easy *data, CURLoption option, const char *ptr) { @@ -1680,12 +1651,12 @@ /* URL decode the components */ if(!result && u) { - Curl_safefree(s->str[STRING_PROXYUSERNAME]); + curlx_safefree(s->str[STRING_PROXYUSERNAME]); result = Curl_urldecode(u, 0, &s->str[STRING_PROXYUSERNAME], NULL, REJECT_ZERO); } if(!result && p) { - Curl_safefree(s->str[STRING_PROXYPASSWORD]); + curlx_safefree(s->str[STRING_PROXYPASSWORD]); result = Curl_urldecode(p, 0, &s->str[STRING_PROXYPASSWORD], NULL, REJECT_ZERO); } @@ -1752,14 +1723,14 @@ /* * 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). + * If the proxy is set to "" (and CURLOPT_PRE_PROXY is set to "" or NULL) + * we explicitly say that we do not want to use a proxy (even though there + * might be environment variables saying so). * * Setting it to NULL, means no proxy but allows the environment variables - * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL). + * to decide for us (if CURLOPT_PRE_PROXY setting it to NULL). */ - return Curl_setstropt(&s->str[STRING_PROXY], ptr); + return setproxy(data, ptr); case CURLOPT_PRE_PROXY: /* * Set proxy server:port to use as SOCKS proxy. @@ -1950,7 +1921,7 @@ */ s->postfields = ptr; /* Release old copied data. */ - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->method = HTTPREQ_POST; break; #endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_MQTT */ @@ -2000,7 +1971,6 @@ * String to set in the HTTP Referer: field. */ result = Curl_setstropt(&s->str[STRING_SET_REFERER], ptr); - Curl_bufref_set(&data->state.referer, s->str[STRING_SET_REFERER], 0, NULL); break; case CURLOPT_USERAGENT: @@ -2218,14 +2188,6 @@ * What range of the file you want to transfer */ return Curl_setstropt(&s->str[STRING_SET_RANGE], ptr); - case CURLOPT_CURLU: - /* - * pass CURLU to set URL - */ - Curl_bufref_free(&data->state.url); - Curl_safefree(s->str[STRING_SET_URL]); - s->uh = (CURLU *)ptr; - break; case CURLOPT_SSLCERT: /* * String that holds filename of the SSL certificate to use @@ -2434,30 +2396,18 @@ break; #endif #endif -#ifdef CURLRES_ARES +#ifdef USE_RESOLV_ARES case CURLOPT_DNS_SERVERS: - result = Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); - if(result) - break; - return Curl_async_ares_set_dns_servers(data); + return Curl_setstropt(&s->str[STRING_DNS_SERVERS], ptr); case CURLOPT_DNS_INTERFACE: - result = Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); - if(result) - break; - return Curl_async_ares_set_dns_interface(data); + return Curl_setstropt(&s->str[STRING_DNS_INTERFACE], ptr); case CURLOPT_DNS_LOCAL_IP4: - result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); - if(result) - break; - return Curl_async_ares_set_dns_local_ip4(data); + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP4], ptr); case CURLOPT_DNS_LOCAL_IP6: - result = Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); - if(result) - break; - return Curl_async_ares_set_dns_local_ip6(data); + return Curl_setstropt(&s->str[STRING_DNS_LOCAL_IP6], ptr); #endif #ifdef USE_UNIX_SOCKETS @@ -2783,7 +2733,7 @@ if(s->postfieldsize < offt && s->postfields == s->str[STRING_COPYPOSTFIELDS]) { /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - Curl_safefree(s->str[STRING_COPYPOSTFIELDS]); + curlx_safefree(s->str[STRING_COPYPOSTFIELDS]); s->postfields = NULL; } s->postfieldsize = offt; @@ -2939,6 +2889,7 @@ case CURLOPT_SHARE: /* CURLSH * */ case CURLOPT_STREAM_DEPENDS: /* CURL * */ case CURLOPT_STREAM_DEPENDS_E: /* CURL * */ + case CURLOPT_CURLU: /* CURLU * */ return setopt_pointers(data, option, param); default: break; @@ -2962,21 +2913,21 @@ */ #undef curl_easy_setopt -CURLcode curl_easy_setopt(CURL *d, CURLoption tag, ...) +CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) { va_list arg; CURLcode result; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; - va_start(arg, tag); + va_start(arg, option); - result = Curl_vsetopt(data, tag, arg); + result = Curl_vsetopt(data, option, arg); va_end(arg); if(result == CURLE_BAD_FUNCTION_ARGUMENT) - failf(data, "setopt 0x%x got bad argument", tag); + failf(data, "setopt 0x%x got bad argument", option); return result; }
diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h index 084cd9f..b5bd733 100644 --- a/Utilities/cmcurl/lib/setup-os400.h +++ b/Utilities/cmcurl/lib/setup-os400.h
@@ -67,22 +67,22 @@ /* GSSAPI wrappers. */ -extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_import_name_a(OM_uint32 *minor_status, gss_buffer_t in_name, gss_OID in_name_type, - gss_name_t * out_name); + gss_name_t *out_name); #define gss_import_name Curl_gss_import_name_a -extern OM_uint32 Curl_gss_display_status_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_display_status_a(OM_uint32 *minor_status, OM_uint32 status_value, int status_type, gss_OID mech_type, - gss_msg_ctx_t * message_context, + gss_msg_ctx_t *message_context, gss_buffer_t status_string); #define gss_display_status Curl_gss_display_status_a -extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 * minor_status, +extern OM_uint32 Curl_gss_init_sec_context_a(OM_uint32 *minor_status, gss_cred_id_t cred_handle, - gss_ctx_id_t * context_handle, + gss_ctx_id_t *context_handle, gss_name_t target_name, gss_OID mech_type, gss_flags_t req_flags, @@ -90,10 +90,10 @@ gss_channel_bindings_t input_chan_bindings, gss_buffer_t input_token, - gss_OID * actual_mech_type, + gss_OID *actual_mech_type, gss_buffer_t output_token, - gss_flags_t * ret_flags, - OM_uint32 * time_rec); + gss_flags_t *ret_flags, + OM_uint32 *time_rec); #define gss_init_sec_context Curl_gss_init_sec_context_a extern OM_uint32 Curl_gss_delete_sec_context_a(OM_uint32 *minor_status,
diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c index 7b06365..d97f45f 100644 --- a/Utilities/cmcurl/lib/sha256.c +++ b/Utilities/cmcurl/lib/sha256.c
@@ -40,10 +40,11 @@ /* Please keep the SSL backend-specific #if branches in this order: * * 1. USE_OPENSSL - * 2. USE_GNUTLS - * 3. USE_MBEDTLS - * 4. USE_COMMON_CRYPTO - * 5. USE_WIN32_CRYPTO + * 2. USE_WOLFSSL + * 3. USE_GNUTLS + * 4. USE_MBEDTLS + * 5. USE_COMMON_CRYPTO + * 6. USE_WIN32_CRYPTO * * This ensures that the same SSL branch gets activated throughout this source * file even if multiple backends are enabled at the same time. @@ -86,6 +87,31 @@ EVP_MD_CTX_destroy(ctx->openssl_ctx); } +#elif defined(USE_WOLFSSL) +#include <wolfssl/options.h> +#include <wolfssl/wolfcrypt/sha256.h> + +typedef struct wc_Sha256 my_sha256_ctx; + +static CURLcode my_sha256_init(void *in) +{ + if(wc_InitSha256(in)) + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static void my_sha256_update(void *in, + const unsigned char *data, + unsigned int length) +{ + (void)wc_Sha256Update(in, data, (word32)length); +} + +static void my_sha256_final(unsigned char *digest, void *in) +{ + (void)wc_Sha256Final(in, digest); +} + #elif defined(USE_GNUTLS) #include <nettle/sha.h> @@ -229,7 +255,7 @@ (a)[0] = (unsigned char)((((unsigned long)(val)) >> 24) & 0xff); \ (a)[1] = (unsigned char)((((unsigned long)(val)) >> 16) & 0xff); \ (a)[2] = (unsigned char)((((unsigned long)(val)) >> 8) & 0xff); \ - (a)[3] = (unsigned char) (((unsigned long)(val)) & 0xff); \ + (a)[3] = (unsigned char)(((unsigned long)(val)) & 0xff); \ } while(0) #define WPA_PUT_BE64(a, val) \ @@ -452,14 +478,19 @@ * Returns CURLE_OK on success. */ CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input, - const size_t len) + size_t len) { CURLcode result; my_sha256_ctx ctx; result = my_sha256_init(&ctx); if(!result) { - my_sha256_update(&ctx, input, curlx_uztoui(len)); + do { + unsigned int ilen = (unsigned int)CURLMIN(len, UINT_MAX); + my_sha256_update(&ctx, input, ilen); + len -= ilen; + input += ilen; + } while(len); my_sha256_final(output, &ctx); } return result;
diff --git a/Utilities/cmcurl/lib/slist.c b/Utilities/cmcurl/lib/slist.c index c6dcdc1..83fd291 100644 --- a/Utilities/cmcurl/lib/slist.c +++ b/Utilities/cmcurl/lib/slist.c
@@ -132,7 +132,7 @@ item = list; do { next = item->next; - Curl_safefree(item->data); + curlx_safefree(item->data); curlx_free(item); item = next; } while(next);
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c index 00297ad..6a97d2e 100644 --- a/Utilities/cmcurl/lib/smb.c +++ b/Utilities/cmcurl/lib/smb.c
@@ -25,7 +25,7 @@ #include "curl_setup.h" #include "urldata.h" -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> /* for htons() */ @@ -376,7 +376,7 @@ struct smb_request *req = entry; (void)key; (void)klen; - Curl_safefree(req->path); + curlx_safefree(req->path); curlx_free(req); } @@ -385,10 +385,10 @@ struct smb_conn *smbc = entry; (void)key; (void)klen; - Curl_safefree(smbc->share); - Curl_safefree(smbc->domain); - Curl_safefree(smbc->recv_buf); - Curl_safefree(smbc->send_buf); + curlx_safefree(smbc->share); + curlx_safefree(smbc->domain); + curlx_safefree(smbc->recv_buf); + curlx_safefree(smbc->send_buf); curlx_free(smbc); } @@ -406,7 +406,7 @@ return result; /* Parse the path for the share */ - Curl_safefree(smbc->share); + curlx_safefree(smbc->share); smbc->share = curlx_strdup((*path == '/' || *path == '\\') ? path + 1 : path); curlx_free(path); @@ -419,7 +419,7 @@ /* The share must be present */ if(!slash) { - Curl_safefree(smbc->share); + curlx_safefree(smbc->share); failf(data, "missing share in URL path for SMB"); return CURLE_URL_MALFORMAT; } @@ -1208,7 +1208,7 @@ /* * SMB handler interface */ -static const struct Curl_protocol Curl_protocol_smb = { +const struct Curl_protocol Curl_protocol_smb = { smb_setup_connection, /* setup_connection */ smb_do, /* do_it */ ZERO_NULL, /* done */ @@ -1223,42 +1223,9 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; -#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */ - -/* - * SMB handler interface - */ -const struct Curl_scheme Curl_scheme_smb = { - "smb", /* scheme */ -#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE) - ZERO_NULL, -#else - &Curl_protocol_smb, -#endif - CURLPROTO_SMB, /* protocol */ - CURLPROTO_SMB, /* family */ - PROTOPT_CONN_REUSE, /* flags */ - PORT_SMB, /* defport */ -}; - -/* - * SMBS handler interface - */ -const struct Curl_scheme Curl_scheme_smbs = { - "smbs", /* scheme */ -#if defined(CURL_DISABLE_SMB) || !defined(USE_CURL_NTLM_CORE) || \ - !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_smb, -#endif - CURLPROTO_SMBS, /* protocol */ - CURLPROTO_SMB, /* family */ - PROTOPT_SSL | PROTOPT_CONN_REUSE, /* flags */ - PORT_SMBS, /* defport */ -}; +#endif /* CURL_ENABLE_SMB && USE_CURL_NTLM_CORE && SIZEOF_CURL_OFF_T > 4 */
diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h index d375404..5a92ad1 100644 --- a/Utilities/cmcurl/lib/smb.h +++ b/Utilities/cmcurl/lib/smb.h
@@ -24,7 +24,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_smb; -extern const struct Curl_scheme Curl_scheme_smbs; +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) +extern const struct Curl_protocol Curl_protocol_smb; +#endif #endif /* HEADER_CURL_SMB_H */
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c index 6088985..b5c425c 100644 --- a/Utilities/cmcurl/lib/smtp.c +++ b/Utilities/cmcurl/lib/smtp.c
@@ -431,7 +431,7 @@ ctx->eos = TRUE; } *peos = (bool)ctx->eos; - DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zd, %d", + DEBUGF(infof(data, "cr_eob_read(%zu) -> %d, %zu, %d", blen, result, *pnread, *peos)); return result; } @@ -485,7 +485,7 @@ const char *line, size_t len, int *resp) { struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN); - bool result = FALSE; + bool end = FALSE; (void)data; DEBUGASSERT(smtpc); @@ -504,7 +504,7 @@ char tmpline[6]; curl_off_t code; const char *p = tmpline; - result = TRUE; + end = TRUE; memcpy(tmpline, line, (len == 5 ? 5 : 3)); tmpline[len == 5 ? 5 : 3] = 0; if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999)) @@ -518,11 +518,11 @@ /* Do we have a multiline (continuation) response? */ else if(line[3] == '-' && (smtpc->state == SMTP_EHLO || smtpc->state == SMTP_COMMAND)) { - result = TRUE; + end = TRUE; *resp = 1; /* Internal response code */ } - return result; + return end; } /*********************************************************************** @@ -1724,7 +1724,7 @@ return CURLE_OK; /* Cleanup our per-request based variables */ - Curl_safefree(smtp->custom); + curlx_safefree(smtp->custom); if(status) { connclose(conn, "SMTP done with bad status"); /* marked for closure */ @@ -1953,7 +1953,7 @@ (void)key; (void)klen; Curl_pp_disconnect(&smtpc->pp); - Curl_safefree(smtpc->domain); + curlx_safefree(smtpc->domain); curlx_free(smtpc); } @@ -1984,7 +1984,7 @@ /* * SMTP protocol handler. */ -static const struct Curl_protocol Curl_protocol_smtp = { +const struct Curl_protocol Curl_protocol_smtp = { smtp_setup_connection, /* setup_connection */ smtp_do, /* do_it */ smtp_done, /* done */ @@ -1999,43 +1999,9 @@ smtp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif /* CURL_DISABLE_SMTP */ - -/* - * SMTP protocol handler. - */ -const struct Curl_scheme Curl_scheme_smtp = { - "smtp", /* scheme */ -#ifdef CURL_DISABLE_SMTP - ZERO_NULL, -#else - &Curl_protocol_smtp, -#endif - CURLPROTO_SMTP, /* protocol */ - CURLPROTO_SMTP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY | /* flags */ - PROTOPT_URLOPTIONS | PROTOPT_SSL_REUSE | PROTOPT_CONN_REUSE, - PORT_SMTP, /* defport */ -}; - -/* - * SMTPS protocol handler. - */ -const struct Curl_scheme Curl_scheme_smtps = { - "smtps", /* scheme */ -#if defined(CURL_DISABLE_SMTP) || !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_smtp, -#endif - CURLPROTO_SMTPS, /* protocol */ - CURLPROTO_SMTP, /* family */ - PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_URLOPTIONS | PROTOPT_CONN_REUSE, - PORT_SMTPS, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/smtp.h b/Utilities/cmcurl/lib/smtp.h index 39f8d78..75b8197 100644 --- a/Utilities/cmcurl/lib/smtp.h +++ b/Utilities/cmcurl/lib/smtp.h
@@ -23,7 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_smtp; -extern const struct Curl_scheme Curl_scheme_smtps; +#ifndef CURL_DISABLE_SMTP +extern const struct Curl_protocol Curl_protocol_smtp; +#endif #endif /* HEADER_CURL_SMTP_H */
diff --git a/Utilities/cmcurl/lib/socketpair.h b/Utilities/cmcurl/lib/socketpair.h index 2aa1733..0427e72 100644 --- a/Utilities/cmcurl/lib/socketpair.h +++ b/Utilities/cmcurl/lib/socketpair.h
@@ -37,7 +37,7 @@ CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all); #else -#define Curl_wakeup_destroy(x) Curl_nop_stmt +#define Curl_wakeup_destroy(x) Curl_nop_stmt #endif #endif /* HEADER_CURL_SOCKETPAIR_H */
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c index b7c3bd6..b3da5be 100644 --- a/Utilities/cmcurl/lib/socks.c +++ b/Utilities/cmcurl/lib/socks.c
@@ -38,6 +38,7 @@ #include "curl_trc.h" #include "select.h" #include "cfilters.h" +#include "cf-dns.h" #include "connect.h" #include "socks.h" #include "curlx/inet_pton.h" @@ -94,18 +95,21 @@ #define SOCKS_CHUNKS 1 -struct socks_state { +struct socks_ctx { enum socks_state_t state; struct bufq iobuf; - const char *hostname; - int remote_port; - const char *proxy_user; - const char *proxy_password; + uint16_t remote_port; + const char *user; + const char *passwd; CURLproxycode presult; + uint32_t resolv_id; + uint8_t ip_version; + uint8_t proxy_type; unsigned char version; BIT(resolve_local); BIT(start_resolving); BIT(socks4a); + char hostname[1]; }; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -127,13 +131,15 @@ *pnread = 0; for(;;) { timediff_t timeout_ms = Curl_timeleft_ms(data); + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + if(timeout_ms < 0) { /* we already got the timeout */ return CURLE_OPERATION_TIMEDOUT; } if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; - if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) + if(SOCKET_READABLE(sock, timeout_ms) <= 0) return CURLE_OPERATION_TIMEDOUT; result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); if(result == CURLE_AGAIN) @@ -162,7 +168,7 @@ #endif /* always use this function to change state, to make debugging easier */ -static void socksstate(struct socks_state *sx, +static void socksstate(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, enum socks_state_t state @@ -189,7 +195,7 @@ #endif } -static CURLproxycode socks_failed(struct socks_state *sx, +static CURLproxycode socks_failed(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, CURLproxycode presult) @@ -199,7 +205,7 @@ return presult; } -static CURLproxycode socks_flush(struct socks_state *sx, +static CURLproxycode socks_flush(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -223,7 +229,7 @@ return CURLPX_OK; } -static CURLproxycode socks_recv(struct socks_state *sx, +static CURLproxycode socks_recv(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, size_t min_bytes, @@ -257,7 +263,7 @@ return CURLPX_OK; } -static CURLproxycode socks4_req_add_hd(struct socks_state *sx, +static CURLproxycode socks4_req_add_hd(struct socks_ctx *sx, struct Curl_easy *data) { unsigned char buf[4]; @@ -276,14 +282,14 @@ return CURLPX_OK; } -static CURLproxycode socks4_req_add_user(struct socks_state *sx, +static CURLproxycode socks4_req_add_user(struct socks_ctx *sx, struct Curl_easy *data) { CURLcode result; size_t nwritten; - if(sx->proxy_user) { - size_t plen = strlen(sx->proxy_user); + if(sx->user) { + size_t plen = strlen(sx->user); if(plen > 255) { /* there is no real size limit to this field in the protocol, but SOCKS5 limits the proxy user field to 255 bytes and it seems likely @@ -292,7 +298,7 @@ return CURLPX_LONG_USER; } /* add proxy name WITH trailing zero */ - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, plen + 1, + result = Curl_bufq_cwrite(&sx->iobuf, sx->user, plen + 1, &nwritten); if(result || (nwritten != (plen + 1))) return CURLPX_SEND_REQUEST; @@ -307,73 +313,58 @@ return CURLPX_OK; } -static CURLproxycode socks4_resolving(struct socks_state *sx, +static CURLproxycode socks4_resolving(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *hp = NULL; + const struct Curl_addrinfo *ai = NULL; CURLcode result; size_t nwritten; + bool dns_done; *done = FALSE; if(sx->start_resolving) { /* need to resolve hostname to add destination address */ sx->start_resolving = FALSE; - DEBUGASSERT(sx->hostname && *sx->hostname); - - result = Curl_resolv(data, sx->hostname, sx->remote_port, - cf->conn->ip_version, TRUE, &dns); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "SOCKS4 non-blocking resolve of %s", sx->hostname); - return CURLPX_OK; + result = Curl_cf_dns_insert_after( + cf, data, Curl_resolv_dns_queries(data, sx->ip_version), + sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + if(result) { + failf(data, "unable to create DNS filter for socks"); + return CURLPX_UNKNOWN_FAIL; } - else if(result) - return CURLPX_RESOLVE_HOST; - } - else { - /* check if we have the name resolved by now */ - result = Curl_resolv_check(data, &dns); - if(!result && !dns) - return CURLPX_OK; } - if(result || !dns) { - failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", sx->hostname); - if(dns) - Curl_resolv_unlink(data, &dns); + /* resolve the hostname by connecting the DNS filter */ + result = Curl_conn_cf_connect(cf->next, data, &dns_done); + if(result) { + failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.", + sx->hostname); return CURLPX_RESOLVE_HOST; } + else if(!dns_done) + return CURLPX_OK; - /* - * We cannot use 'hostent' as a struct that Curl_resolv() returns. It - * returns a Curl_addrinfo pointer that may not always look the same. - */ - /* scan for the first IPv4 address */ - hp = dns->addr; - while(hp && (hp->ai_family != AF_INET)) - hp = hp->ai_next; - - if(hp) { + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + if(ai) { struct sockaddr_in *saddr_in; char ipbuf[64]; - Curl_printable_address(hp, ipbuf, sizeof(ipbuf)); + Curl_printable_address(ai, ipbuf, sizeof(ipbuf)); CURL_TRC_CF(data, cf, "SOCKS4 connect to IPv4 %s (locally resolved)", ipbuf); - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; + saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; result = Curl_bufq_write(&sx->iobuf, (unsigned char *)&saddr_in->sin_addr.s_addr, 4, &nwritten); - Curl_resolv_unlink(data, &dns); /* not used anymore from now on */ if(result || (nwritten != 4)) return CURLPX_SEND_REQUEST; } else { - Curl_resolv_unlink(data, &dns); + /* No ipv4 address resolved */ failf(data, "SOCKS4 connection to %s not supported", sx->hostname); return CURLPX_RESOLVE_HOST; } @@ -382,7 +373,7 @@ return CURLPX_OK; } -static CURLproxycode socks4_check_resp(struct socks_state *sx, +static CURLproxycode socks4_check_resp(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -431,7 +422,7 @@ "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)" ", request rejected or failed.", resp[4], resp[5], resp[6], resp[7], - ((resp[2] << 8) | resp[3]), resp[1]); + (unsigned int)((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_REQUEST_FAILED; case 92: failf(data, @@ -439,7 +430,7 @@ ", request rejected because SOCKS server cannot connect to " "identd on the client.", resp[4], resp[5], resp[6], resp[7], - ((resp[2] << 8) | resp[3]), resp[1]); + (unsigned int)((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_IDENTD; case 93: failf(data, @@ -447,14 +438,14 @@ ", request rejected because the client program and identd " "report different user-ids.", resp[4], resp[5], resp[6], resp[7], - ((resp[2] << 8) | resp[3]), resp[1]); + (unsigned int)((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_IDENTD_DIFFER; default: failf(data, "[SOCKS] cannot complete SOCKS4 connection to %u.%u.%u.%u:%u. (%u)" ", Unknown.", resp[4], resp[5], resp[6], resp[7], - ((resp[2] << 8) | resp[3]), resp[1]); + (unsigned int)((resp[2] << 8) | resp[3]), resp[1]); return CURLPX_UNKNOWN_FAIL; } } @@ -471,7 +462,7 @@ * Nonsupport "Identification Protocol (RFC1413)" */ static CURLproxycode socks4_connect(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { size_t nwritten; @@ -489,16 +480,14 @@ case SOCKS4_ST_START: Curl_bufq_reset(&sx->iobuf); sx->start_resolving = FALSE; - sx->socks4a = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS4A); + sx->socks4a = (sx->proxy_type == CURLPROXY_SOCKS4A); sx->resolve_local = !sx->socks4a; sx->presult = CURLPX_OK; /* SOCKS4 can only do IPv4, insist! */ - cf->conn->ip_version = CURL_IPRESOLVE_V4; - CURL_TRC_CF(data, cf, "SOCKS4%s communication to%s %s:%d", - sx->socks4a ? "a" : "", - cf->conn->bits.httpproxy ? " HTTP proxy" : "", - sx->hostname, sx->remote_port); + sx->ip_version = CURL_IPRESOLVE_V4; + CURL_TRC_CF(data, cf, "SOCKS4%s connecting to %s:%u", + sx->socks4a ? "a" : "", sx->hostname, sx->remote_port); /* * Compose socks4 request @@ -591,7 +580,7 @@ } static CURLproxycode socks5_req0_init(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { const unsigned char auth = data->set.socks5auth; @@ -613,7 +602,7 @@ "CURLOPT_SOCKS5_AUTH: %u", auth); if(!(auth & CURLAUTH_BASIC)) /* disable username/password auth */ - sx->proxy_user = NULL; + sx->user = NULL; req[0] = 5; /* version */ nauths = 1; @@ -624,7 +613,7 @@ req[1 + nauths] = 1; /* GSS-API */ } #endif - if(sx->proxy_user) { + if(sx->user) { ++nauths; req[1 + nauths] = 2; /* username/password */ } @@ -637,7 +626,7 @@ return CURLPX_OK; } -static CURLproxycode socks5_check_resp0(struct socks_state *sx, +static CURLproxycode socks5_check_resp0(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -689,7 +678,7 @@ } static CURLproxycode socks5_auth_init(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { /* Needs username and password */ @@ -697,9 +686,9 @@ unsigned char buf[2]; CURLcode result; - if(sx->proxy_user && sx->proxy_password) { - ulen = strlen(sx->proxy_user); - plen = strlen(sx->proxy_password); + if(sx->user && sx->passwd) { + ulen = strlen(sx->user); + plen = strlen(sx->passwd); /* the lengths must fit in a single byte */ if(ulen > 255) { failf(data, "Excessive username length for proxy auth"); @@ -724,7 +713,7 @@ if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; if(ulen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_user, ulen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->user, ulen, &nwritten); if(result || (nwritten != ulen)) return CURLPX_SEND_REQUEST; } @@ -733,7 +722,7 @@ if(result || (nwritten != 1)) return CURLPX_SEND_REQUEST; if(plen) { - result = Curl_bufq_cwrite(&sx->iobuf, sx->proxy_password, plen, &nwritten); + result = Curl_bufq_cwrite(&sx->iobuf, sx->passwd, plen, &nwritten); if(result || (nwritten != plen)) return CURLPX_SEND_REQUEST; } @@ -741,7 +730,7 @@ return CURLPX_OK; } -static CURLproxycode socks5_check_auth_resp(struct socks_state *sx, +static CURLproxycode socks5_check_auth_resp(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -766,7 +755,7 @@ return CURLPX_OK; } -static CURLproxycode socks5_req1_init(struct socks_state *sx, +static CURLproxycode socks5_req1_init(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -790,7 +779,7 @@ /* remote resolving, send what type+addr/string to resolve */ #ifdef USE_IPV6 - if(cf->conn->bits.ipv6_ip) { + if(strchr(sx->hostname, ':')) { desttype = 4; destination = ipbuf; destlen = 16; @@ -806,6 +795,10 @@ } else { const size_t hostname_len = strlen(sx->hostname); + /* socks5_req0_init() already rejects hostnames longer than 255 bytes, so + this cast to unsigned char is safe. Assert to guard against future + refactoring that might remove or reorder that earlier check. */ + DEBUGASSERT(hostname_len <= 255); desttype = 3; destination = (const unsigned char *)sx->hostname; destlen = (unsigned char)hostname_len; /* one byte length */ @@ -826,18 +819,17 @@ result = Curl_bufq_write(&sx->iobuf, req, 2, &nwritten); if(result || (nwritten != 2)) return CURLPX_SEND_REQUEST; - CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (remotely resolved)", + CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (remotely resolved)", sx->hostname, sx->remote_port); return CURLPX_OK; } -static CURLproxycode socks5_resolving(struct socks_state *sx, +static CURLproxycode socks5_resolving(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) { - struct Curl_dns_entry *dns = NULL; - struct Curl_addrinfo *hp = NULL; + const struct Curl_addrinfo *ai = NULL; char dest[MAX_IPADR_LEN]; /* printable address */ const unsigned char *destination = NULL; unsigned char desttype = 1, destlen = 4; @@ -845,71 +837,62 @@ CURLcode result; CURLproxycode presult = CURLPX_OK; size_t nwritten; + bool dns_done; *done = FALSE; if(sx->start_resolving) { /* need to resolve hostname to add destination address */ sx->start_resolving = FALSE; - DEBUGASSERT(sx->hostname && *sx->hostname); - - result = Curl_resolv(data, sx->hostname, sx->remote_port, - cf->conn->ip_version, TRUE, &dns); - if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "SOCKS5 non-blocking resolve of %s", sx->hostname); - return CURLPX_OK; + result = Curl_cf_dns_insert_after( + cf, data, Curl_resolv_dns_queries(data, sx->ip_version), + sx->hostname, sx->remote_port, TRNSPRT_TCP, TRUE); + if(result) { + failf(data, "unable to create DNS filter for socks"); + return CURLPX_UNKNOWN_FAIL; } - else if(result) - return CURLPX_RESOLVE_HOST; - } - else { - /* check if we have the name resolved by now */ - result = Curl_resolv_check(data, &dns); - if(!result && !dns) - return CURLPX_OK; } - if(result || !dns) { + /* resolve the hostname by connecting the DNS filter */ + result = Curl_conn_cf_connect(cf->next, data, &dns_done); + if(result) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); - presult = CURLPX_RESOLVE_HOST; - goto out; + return CURLPX_RESOLVE_HOST; } + else if(!dns_done) + return CURLPX_OK; - if(dns) - hp = dns->addr; #ifdef USE_IPV6 - if(data->set.ipver != CURL_IPRESOLVE_WHATEVER) { - int wanted_family = data->set.ipver == CURL_IPRESOLVE_V4 ? - AF_INET : AF_INET6; - /* scan for the first proper address */ - while(hp && (hp->ai_family != wanted_family)) - hp = hp->ai_next; - } + if(data->set.ipver != CURL_IPRESOLVE_V4) + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET6, 0); #endif - if(!hp) { + if(!ai) + ai = Curl_cf_dns_get_ai(cf->next, data, AF_INET, 0); + + if(!ai) { failf(data, "Failed to resolve \"%s\" for SOCKS5 connect.", sx->hostname); presult = CURLPX_RESOLVE_HOST; goto out; } - Curl_printable_address(hp, dest, sizeof(dest)); + Curl_printable_address(ai, dest, sizeof(dest)); - if(hp->ai_family == AF_INET) { + if(ai->ai_family == AF_INET) { struct sockaddr_in *saddr_in; desttype = 1; /* ATYP: IPv4 = 1 */ destlen = 4; - saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr; + saddr_in = (struct sockaddr_in *)(void *)ai->ai_addr; destination = (const unsigned char *)&saddr_in->sin_addr.s_addr; - CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%d (locally resolved)", + CURL_TRC_CF(data, cf, "SOCKS5 connect to %s:%u (locally resolved)", dest, sx->remote_port); } #ifdef USE_IPV6 - else if(hp->ai_family == AF_INET6) { + else if(ai->ai_family == AF_INET6) { struct sockaddr_in6 *saddr_in6; desttype = 4; /* ATYP: IPv6 = 4 */ destlen = 16; - saddr_in6 = (struct sockaddr_in6 *)(void *)hp->ai_addr; + saddr_in6 = (struct sockaddr_in6 *)(void *)ai->ai_addr; destination = (const unsigned char *)&saddr_in6->sin6_addr.s6_addr; - CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%d (locally resolved)", + CURL_TRC_CF(data, cf, "SOCKS5 connect to [%s]:%u (locally resolved)", dest, sx->remote_port); } #endif @@ -941,13 +924,11 @@ } out: - if(dns) - Curl_resolv_unlink(data, &dns); *done = (presult == CURLPX_OK); return presult; } -static CURLproxycode socks5_recv_resp1(struct socks_state *sx, +static CURLproxycode socks5_recv_resp1(struct socks_ctx *sx, struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -1046,7 +1027,7 @@ * destination server. */ static CURLproxycode socks5_connect(struct Curl_cfilter *cf, - struct socks_state *sx, + struct socks_ctx *sx, struct Curl_easy *data) { CURLproxycode presult; @@ -1056,14 +1037,13 @@ switch(sx->state) { case SOCKS_ST_INIT: sx->version = 5; - sx->resolve_local = (cf->conn->socks_proxy.proxytype == CURLPROXY_SOCKS5); + sx->resolve_local = (sx->proxy_type == CURLPROXY_SOCKS5); sxstate(sx, cf, data, SOCKS5_ST_START); FALLTHROUGH(); case SOCKS5_ST_START: - if(cf->conn->bits.httpproxy) - CURL_TRC_CF(data, cf, "SOCKS5: connecting to HTTP proxy %s port %d", - sx->hostname, sx->remote_port); + CURL_TRC_CF(data, cf, "SOCKS5: connecting to %s:%u", + sx->hostname, sx->remote_port); presult = socks5_req0_init(cf, sx, data); if(presult) return socks_failed(sx, cf, data, presult); @@ -1198,13 +1178,11 @@ } } -static void socks_proxy_cf_free(struct Curl_cfilter *cf) +static void socks_proxy_ctx_free(struct socks_ctx *ctx) { - struct socks_state *sxstate = cf->ctx; - if(sxstate) { - Curl_bufq_free(&sxstate->iobuf); - curlx_free(sxstate); - cf->ctx = NULL; + if(ctx) { + Curl_bufq_free(&ctx->iobuf); + curlx_free(ctx); } } @@ -1219,11 +1197,9 @@ struct Curl_easy *data, bool *done) { - CURLcode result; - struct connectdata *conn = cf->conn; - int sockindex = cf->sockindex; - struct socks_state *sx = cf->ctx; + struct socks_ctx *ctx = cf->ctx; CURLproxycode pxresult = CURLPX_OK; + CURLcode result; if(cf->connected) { *done = TRUE; @@ -1234,47 +1210,19 @@ if(result || !*done) return result; - if(!sx) { - cf->ctx = sx = curlx_calloc(1, sizeof(*sx)); - if(!sx) { - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - /* for the secondary socket (FTP), use the "connect to host" - * but ignore the "connect to port" (use the secondary port) - */ - sx->hostname = - conn->bits.httpproxy ? - conn->http_proxy.host.name : - conn->bits.conn_to_host ? - conn->conn_to_host.name : - sockindex == SECONDARYSOCKET ? - conn->secondaryhostname : conn->host.name; - sx->remote_port = - conn->bits.httpproxy ? (int)conn->http_proxy.port : - sockindex == SECONDARYSOCKET ? conn->secondary_port : - conn->bits.conn_to_port ? conn->conn_to_port : - conn->remote_port; - sx->proxy_user = conn->socks_proxy.user; - sx->proxy_password = conn->socks_proxy.passwd; - Curl_bufq_init2(&sx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, - BUFQ_OPT_SOFT_LIMIT); - } - - switch(conn->socks_proxy.proxytype) { + switch(ctx->proxy_type) { case CURLPROXY_SOCKS5: case CURLPROXY_SOCKS5_HOSTNAME: - pxresult = socks5_connect(cf, sx, data); + pxresult = socks5_connect(cf, ctx, data); break; case CURLPROXY_SOCKS4: case CURLPROXY_SOCKS4A: - pxresult = socks4_connect(cf, sx, data); + pxresult = socks4_connect(cf, ctx, data); break; default: - failf(data, "unknown proxytype option given"); + DEBUGASSERT(0); /* should not come here, checked it at creation time */ result = CURLE_COULDNT_CONNECT; goto out; } @@ -1284,7 +1232,7 @@ data->info.pxcode = pxresult; goto out; } - else if(sx->state != SOCKS_ST_SUCCESS) + else if(ctx->state != SOCKS_ST_SUCCESS) goto out; #ifdef CURLVERBOSE @@ -1292,22 +1240,25 @@ struct ip_quadruple ipquad; bool is_ipv6; if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) - infof(data, "Opened %sSOCKS connection from %s port %u to %s port %u " + infof(data, "Opened %sSOCKS connection from %s port %d to %s port %d " "(via %s port %u)", - (sockindex == SECONDARYSOCKET) ? "2nd " : "", + (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", ipquad.local_ip, ipquad.local_port, - sx->hostname, sx->remote_port, + ctx->hostname, ctx->remote_port, ipquad.remote_ip, ipquad.remote_port); else infof(data, "Opened %sSOCKS connection", - (sockindex == SECONDARYSOCKET) ? "2nd " : ""); + (cf->sockindex == SECONDARYSOCKET) ? "2nd " : ""); } #endif - socks_proxy_cf_free(cf); cf->connected = TRUE; out: *done = (bool)cf->connected; + if(*done || result) { + ctx->user = NULL; + ctx->passwd = NULL; + } return result; } @@ -1315,7 +1266,7 @@ struct Curl_easy *data, struct easy_pollset *ps) { - struct socks_state *sx = cf->ctx; + struct socks_ctx *sx = cf->ctx; CURLcode result = CURLE_OK; if(!cf->connected && sx) { @@ -1342,24 +1293,24 @@ static void socks_proxy_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { - DEBUGASSERT(cf->next); cf->connected = FALSE; - socks_proxy_cf_free(cf); - cf->next->cft->do_close(cf->next, data); + if(cf->next) + cf->next->cft->do_close(cf->next, data); } static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { (void)data; - socks_proxy_cf_free(cf); + socks_proxy_ctx_free(cf->ctx); + cf->ctx = NULL; } static CURLcode socks_cf_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2) { - struct socks_state *sx = cf->ctx; + struct socks_ctx *sx = cf->ctx; switch(query) { case CF_QUERY_HOST_PORT: @@ -1402,15 +1353,53 @@ }; CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data) + struct Curl_easy *data, + const char *hostname, + uint16_t port, + uint8_t ip_version, + uint8_t proxy_type, + const char *user, + const char *passwd) { struct Curl_cfilter *cf; + struct socks_ctx *ctx; + size_t hostlen = hostname ? strlen(hostname) : 0; CURLcode result; - (void)data; - result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, NULL); + if(!hostlen) + return CURLE_FAILED_INIT; + + switch(proxy_type) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + break; /* all supported */ + default: + failf(data, "unknown proxytype %d option given", proxy_type); + return CURLE_COULDNT_CONNECT; + } + + /* NUL byte already part of struct size */ + ctx = curlx_calloc(1, sizeof(*ctx) + hostlen); + if(!ctx) { + return CURLE_OUT_OF_MEMORY; + } + + memcpy(ctx->hostname, hostname, hostlen); + ctx->remote_port = port; + ctx->ip_version = ip_version; + ctx->proxy_type = proxy_type; + ctx->user = user; + ctx->passwd = passwd; + Curl_bufq_init2(&ctx->iobuf, SOCKS_CHUNK_SIZE, SOCKS_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); + + result = Curl_cf_create(&cf, &Curl_cft_socks_proxy, ctx); if(!result) Curl_conn_cf_insert_after(cf_at, cf); + else + socks_proxy_ctx_free(ctx); return result; }
diff --git a/Utilities/cmcurl/lib/socks.h b/Utilities/cmcurl/lib/socks.h index 520fb75..ea36832 100644 --- a/Utilities/cmcurl/lib/socks.h +++ b/Utilities/cmcurl/lib/socks.h
@@ -46,8 +46,19 @@ struct Curl_easy *data); #endif +/* Insert a SOCKS filter after `cf_at` for connecting to `hostname` + * and `port` with optional credentials. + * Credentials are NOT duplicated and are + * expected to exist during connect phase. + */ CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, - struct Curl_easy *data); + struct Curl_easy *data, + const char *hostname, + uint16_t port, + uint8_t ip_version, + uint8_t proxy_type, + const char *user, + const char *passwd); extern struct Curl_cftype Curl_cft_socks_proxy;
diff --git a/Utilities/cmcurl/lib/socks_gssapi.c b/Utilities/cmcurl/lib/socks_gssapi.c index 962fcd0..32db070 100644 --- a/Utilities/cmcurl/lib/socks_gssapi.c +++ b/Utilities/cmcurl/lib/socks_gssapi.c
@@ -35,7 +35,7 @@ #include "socks.h" #include "curlx/strdup.h" -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -106,7 +106,7 @@ CURLcode code; size_t actualread; size_t nwritten; - int result; + CURLcode result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; int gss_conf_state, gss_enc; @@ -156,7 +156,7 @@ GSS_C_NT_HOSTBASED_SERVICE, &server); } - Curl_safefree(service.value); + curlx_safefree(service.value); service.length = 0; if(check_gss_err(data, gss_major_status, @@ -183,7 +183,7 @@ &gss_ret_flags); if(gss_token != GSS_C_NO_BUFFER) { - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; } if(check_gss_err(data, gss_major_status, @@ -228,7 +228,7 @@ if(gss_major_status != GSS_S_CONTINUE_NEEDED) break; - /* analyse response */ + /* analyze response */ /* GSS-API response looks like * +----+------+-----+----------------+ @@ -266,6 +266,13 @@ memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length GSS-API authentication token."); + gss_release_name(&gss_status, &server); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + gss_recv_token.length = us_length; gss_recv_token.value = curlx_malloc(gss_recv_token.length); if(!gss_recv_token.value) { @@ -283,7 +290,7 @@ if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API authentication token."); gss_release_name(&gss_status, &server); - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; @@ -385,14 +392,14 @@ &gss_conf_state, &gss_w_token); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { - Curl_safefree(gss_send_token.value); + curlx_safefree(gss_send_token.value); gss_send_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to wrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } - Curl_safefree(gss_send_token.value); + curlx_safefree(gss_send_token.value); gss_send_token.length = 0; us_length = htons((unsigned short)gss_w_token.length); @@ -453,6 +460,12 @@ memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length GSS-API encryption token."); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); + return CURLE_COULDNT_CONNECT; + } + gss_recv_token.length = us_length; gss_recv_token.value = curlx_malloc(gss_recv_token.length); if(!gss_recv_token.value) { @@ -464,7 +477,7 @@ if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API encryption type."); - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; @@ -476,14 +489,14 @@ 0, GSS_C_QOP_DEFAULT); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; gss_release_buffer(&gss_status, &gss_w_token); Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to unwrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; if(gss_w_token.length != 1) { @@ -501,14 +514,14 @@ if(gss_recv_token.length != 1) { failf(data, "Invalid GSS-API encryption response length (%zu).", gss_recv_token.length); - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } memcpy(socksreq, gss_recv_token.value, gss_recv_token.length); - Curl_safefree(gss_recv_token.value); + curlx_safefree(gss_recv_token.value); gss_recv_token.length = 0; } @@ -526,7 +539,7 @@ return CURLE_OK; } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/socks_sspi.c b/Utilities/cmcurl/lib/socks_sspi.c index cc0e043..385312a 100644 --- a/Utilities/cmcurl/lib/socks_sspi.c +++ b/Utilities/cmcurl/lib/socks_sspi.c
@@ -159,7 +159,7 @@ sspi_ret_flagsp, NULL); curlx_free(sname); - Curl_safefree(sspi_recv_token.pvBuffer); + curlx_safefree(sspi_recv_token.pvBuffer); sspi_recv_token.cbBuffer = 0; if(check_sspi_err(data, status, "InitializeSecurityContext")) { @@ -196,7 +196,7 @@ socks5_free_token(&sspi_send_token, CURLE_OK); sspi_send_token.cbBuffer = 0; - Curl_safefree(sspi_recv_token.pvBuffer); + curlx_safefree(sspi_recv_token.pvBuffer); sspi_recv_token.cbBuffer = 0; if(status != SEC_I_CONTINUE_NEEDED) @@ -223,6 +223,11 @@ memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length SSPI authentication token."); + return CURLE_COULDNT_CONNECT; + } + sspi_recv_token.cbBuffer = us_length; sspi_recv_token.pvBuffer = curlx_malloc(us_length); @@ -247,9 +252,9 @@ static CURLcode socks5_free(SecBuffer *sspi_w_token, CURLcode result) { - Curl_safefree(sspi_w_token[0].pvBuffer); - Curl_safefree(sspi_w_token[1].pvBuffer); - Curl_safefree(sspi_w_token[2].pvBuffer); + curlx_safefree(sspi_w_token[0].pvBuffer); + curlx_safefree(sspi_w_token[1].pvBuffer); + curlx_safefree(sspi_w_token[2].pvBuffer); return result; } @@ -400,6 +405,11 @@ memcpy(&us_length, socksreq + 2, sizeof(short)); us_length = ntohs(us_length); + if(!us_length) { + failf(data, "Invalid zero-length SSPI encryption token."); + return CURLE_COULDNT_CONNECT; + } + sspi_w_token[0].cbBuffer = us_length; sspi_w_token[0].pvBuffer = curlx_malloc(us_length); if(!sspi_w_token[0].pvBuffer) @@ -488,7 +498,7 @@ if(result) goto error; - Curl_safefree(service_name); + curlx_safefree(service_name); status = Curl_pSecFn->QueryCredentialsAttributes(&cred_handle, SECPKG_CRED_ATTR_NAMES,
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c index 5bae99e..c5ce9c2 100644 --- a/Utilities/cmcurl/lib/telnet.c +++ b/Utilities/cmcurl/lib/telnet.c
@@ -1266,9 +1266,9 @@ #ifdef USE_WINSOCK /* We want to wait for both stdin and the socket. Since - ** the select() function in Winsock only works on sockets - ** we have to use the WaitForMultipleObjects() call. - */ + * the select() function in Winsock only works on sockets + * we have to use the WaitForMultipleObjects() call. + */ /* First, create a sockets event object */ event_handle = WSACreateEvent(); @@ -1569,7 +1569,7 @@ /* * TELNET protocol handler. */ -static const struct Curl_protocol Curl_protocol_telnet = { +const struct Curl_protocol Curl_protocol_telnet = { ZERO_NULL, /* setup_connection */ telnet_do, /* do_it */ telnet_done, /* done */ @@ -1584,25 +1584,9 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif /* !CURL_DISABLE_TELNET */ - -/* - * TELNET protocol handler. - */ -const struct Curl_scheme Curl_scheme_telnet = { - "telnet", /* scheme */ -#ifdef CURL_DISABLE_TELNET - ZERO_NULL, -#else - &Curl_protocol_telnet, -#endif - CURLPROTO_TELNET, /* protocol */ - CURLPROTO_TELNET, /* family */ - PROTOPT_NONE | PROTOPT_NOURLQUERY, /* flags */ - PORT_TELNET, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/telnet.h b/Utilities/cmcurl/lib/telnet.h index c106179..3848fff 100644 --- a/Utilities/cmcurl/lib/telnet.h +++ b/Utilities/cmcurl/lib/telnet.h
@@ -23,6 +23,8 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_telnet; +#ifndef CURL_DISABLE_TELNET +extern const struct Curl_protocol Curl_protocol_telnet; +#endif #endif /* HEADER_CURL_TELNET_H */
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c index cc0769b..6cc672d 100644 --- a/Utilities/cmcurl/lib/tftp.c +++ b/Utilities/cmcurl/lib/tftp.c
@@ -175,7 +175,7 @@ /* Average reposting an ACK after 5 seconds */ state->retry_max = (int)timeout / 5; - /* But bound the total number */ + /* Bound the total number */ if(state->retry_max < 3) state->retry_max = 3; @@ -300,8 +300,8 @@ return CURLE_TFTP_ILLEGAL; } - state->blksize = (int)blksize; - infof(data, "blksize parsed from OACK (%d) requested (%d)", + state->blksize = (unsigned int)blksize; + infof(data, "blksize parsed from OACK (%u) requested (%u)", state->blksize, state->requested_blksize); } else if(checkprefix(TFTP_OPTION_TSIZE, option)) { @@ -370,9 +370,9 @@ int rblock = getrpacketblock(&state->rpacket); if(rblock != state->block && - /* There is a bug in tftpd-hpa that causes it to send us an ack for - * 65535 when the block number wraps to 0. So when we are expecting - * 0, also accept 65535. See + /* There is a bug in tftpd-hpa that causes it to send us an ACK for + * 65535 when the block number wraps to 0. To handle it, when we are + * expecting 0, also accept 65535. See * https://www.syslinux.org/archives/2010-September/015612.html * */ !(state->block == 0 && rblock == 65535)) { @@ -418,7 +418,7 @@ return CURLE_OK; } - /* TFTP considers data block size < 512 bytes as an end of session. So + /* TFTP considers data block size < 512 bytes as an end of session, so * in some cases we must wait for additional data to build full (512 bytes) * data block. * */ @@ -717,7 +717,7 @@ result = tftp_option_add(state, &sbytes, sbytes, buf); /* add blksize option */ - curl_msnprintf(buf, sizeof(buf), "%d", state->requested_blksize); + curl_msnprintf(buf, sizeof(buf), "%u", state->requested_blksize); if(result == CURLE_OK) result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_BLKSIZE); if(result == CURLE_OK) @@ -883,8 +883,8 @@ struct tftp_conn *state = entry; (void)key; (void)klen; - Curl_safefree(state->rpacket.data); - Curl_safefree(state->spacket.data); + curlx_safefree(state->rpacket.data); + curlx_safefree(state->spacket.data); curlx_free(state); } @@ -1332,7 +1332,7 @@ /* * TFTP protocol handler. */ -static const struct Curl_protocol Curl_protocol_tftp = { +const struct Curl_protocol Curl_protocol_tftp = { tftp_setup_connection, /* setup_connection */ tftp_do, /* do_it */ tftp_done, /* done */ @@ -1347,25 +1347,9 @@ ZERO_NULL, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; #endif - -/* - * TFTP protocol handler. - */ -const struct Curl_scheme Curl_scheme_tftp = { - "tftp", /* scheme */ -#ifdef CURL_DISABLE_TFTP - ZERO_NULL, -#else - &Curl_protocol_tftp, -#endif - CURLPROTO_TFTP, /* protocol */ - CURLPROTO_TFTP, /* family */ - PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY, /* flags */ - PORT_TFTP, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/tftp.h b/Utilities/cmcurl/lib/tftp.h index 91ecd29..3231010 100644 --- a/Utilities/cmcurl/lib/tftp.h +++ b/Utilities/cmcurl/lib/tftp.h
@@ -23,7 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ -extern const struct Curl_scheme Curl_scheme_tftp; +#ifndef CURL_DISABLE_TFTP +extern const struct Curl_protocol Curl_protocol_tftp; +#endif #define TFTP_BLKSIZE_MIN 8 #define TFTP_BLKSIZE_MAX 65464
diff --git a/Utilities/cmcurl/lib/thrdpool.c b/Utilities/cmcurl/lib/thrdpool.c new file mode 100644 index 0000000..22faa39 --- /dev/null +++ b/Utilities/cmcurl/lib/thrdpool.c
@@ -0,0 +1,481 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_THREADS + +#include "llist.h" +#include "curl_threads.h" +#include "curlx/timeval.h" +#include "thrdpool.h" +#ifdef CURLVERBOSE +#include "curl_trc.h" +#include "urldata.h" +#endif + + +struct thrdslot { + struct Curl_llist_node node; + struct curl_thrdpool *tpool; + curl_thread_t thread; + curl_cond_t await; + struct curltime starttime; + const char *work_description; + timediff_t work_timeout_ms; + uint32_t id; + BIT(running); + BIT(idle); +}; + +struct curl_thrdpool { + char *name; + uint64_t refcount; + curl_mutex_t lock; + curl_cond_t await; + struct Curl_llist slots; + struct Curl_llist zombies; + Curl_thrdpool_take_item_cb *fn_take; + Curl_thrdpool_process_item_cb *fn_process; + Curl_thrdpool_return_item_cb *fn_return; + void *fn_user_data; + CURLcode fatal_err; + uint32_t min_threads; + uint32_t max_threads; + uint32_t idle_time_ms; + uint32_t next_id; + BIT(aborted); + BIT(detached); +}; + +static void thrdpool_join_zombies(struct curl_thrdpool *tpool); +static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked); + +static void thrdslot_destroy(struct thrdslot *tslot) +{ + DEBUGASSERT(tslot->thread == curl_thread_t_null); + DEBUGASSERT(!tslot->running); + Curl_cond_destroy(&tslot->await); + curlx_free(tslot); +} + +static void thrdslot_done(struct thrdslot *tslot) +{ + struct curl_thrdpool *tpool = tslot->tpool; + + DEBUGASSERT(Curl_node_llist(&tslot->node) == &tpool->slots); + Curl_node_remove(&tslot->node); + tslot->running = FALSE; + Curl_llist_append(&tpool->zombies, tslot, &tslot->node); + Curl_cond_signal(&tpool->await); +} + +static CURL_THREAD_RETURN_T CURL_STDCALL thrdslot_run(void *arg) +{ + struct thrdslot *tslot = arg; + struct curl_thrdpool *tpool = tslot->tpool; + void *item; + + Curl_mutex_acquire(&tpool->lock); + DEBUGASSERT(Curl_node_llist(&tslot->node) == &tpool->slots); + for(;;) { + while(!tpool->aborted) { + tslot->work_description = NULL; + tslot->work_timeout_ms = 0; + item = tpool->fn_take(tpool->fn_user_data, &tslot->work_description, + &tslot->work_timeout_ms); + if(!item) + break; + tslot->starttime = curlx_now(); + tslot->idle = FALSE; + Curl_mutex_release(&tpool->lock); + + tpool->fn_process(item); + + Curl_mutex_acquire(&tpool->lock); + tslot->work_description = NULL; + tpool->fn_return(item, tpool->aborted ? NULL : tpool->fn_user_data); + } + + if(tpool->aborted || + (Curl_llist_count(&tpool->slots) > tpool->max_threads)) + goto out; + + tslot->idle = TRUE; + tslot->starttime = curlx_now(); + thrdpool_join_zombies(tpool); + Curl_cond_signal(&tpool->await); + /* Only wait with idle timeout when we are above the minimum + * number of threads. Otherwise short idle timeouts will keep + * on activating threads that have no means to shut down. */ + if((tpool->idle_time_ms > 0) && + (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { + CURLcode r = Curl_cond_timedwait(&tslot->await, &tpool->lock, + tpool->idle_time_ms); + if((r == CURLE_OPERATION_TIMEDOUT) && + (Curl_llist_count(&tpool->slots) > tpool->min_threads)) { + goto out; + } + } + else { + Curl_cond_wait(&tslot->await, &tpool->lock); + } + } + +out: + thrdslot_done(tslot); + if(!thrdpool_unlink(tslot->tpool, TRUE)) { + /* tpool not destroyed */ + Curl_mutex_release(&tpool->lock); + } + return 0; +} + +static CURLcode thrdslot_start(struct curl_thrdpool *tpool) +{ + struct thrdslot *tslot; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tslot = curlx_calloc(1, sizeof(*tslot)); + if(!tslot) + goto out; + tslot->id = tpool->next_id++; + tslot->tpool = tpool; + tslot->thread = curl_thread_t_null; + Curl_cond_init(&tslot->await); + + tpool->refcount++; + tslot->running = TRUE; + tslot->thread = Curl_thread_create(thrdslot_run, tslot); + if(tslot->thread == curl_thread_t_null) { /* never started */ + tslot->running = FALSE; + thrdpool_unlink(tpool, TRUE); + result = CURLE_FAILED_INIT; + goto out; + } + + Curl_llist_append(&tpool->slots, tslot, &tslot->node); + tslot = NULL; + result = CURLE_OK; + +out: + if(tslot) + thrdslot_destroy(tslot); + return result; +} + +static void thrdpool_wake_all(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + Curl_cond_signal(&tslot->await); + } +} + +static void thrdpool_join_zombies(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + + for(e = Curl_llist_head(&tpool->zombies); e; + e = Curl_llist_head(&tpool->zombies)) { + struct thrdslot *tslot = Curl_node_elem(e); + + Curl_node_remove(&tslot->node); + if(tslot->thread != curl_thread_t_null) { + Curl_mutex_release(&tpool->lock); + Curl_thread_join(&tslot->thread); + Curl_mutex_acquire(&tpool->lock); + tslot->thread = curl_thread_t_null; + } + thrdslot_destroy(tslot); + } +} + +static bool thrdpool_unlink(struct curl_thrdpool *tpool, bool locked) +{ + DEBUGASSERT(tpool->refcount); + if(tpool->refcount) + tpool->refcount--; + if(tpool->refcount) + return FALSE; + + /* no more references, free */ + DEBUGASSERT(tpool->aborted); + thrdpool_join_zombies(tpool); + if(locked) + Curl_mutex_release(&tpool->lock); + curlx_free(tpool->name); + Curl_cond_destroy(&tpool->await); + Curl_mutex_destroy(&tpool->lock); + curlx_free(tpool); + return TRUE; +} + +static CURLcode thrdpool_signal(struct curl_thrdpool *tpool, + uint32_t nthreads) +{ + struct Curl_llist_node *e, *n; + CURLcode result = CURLE_OK; + + DEBUGASSERT(!tpool->aborted); + thrdpool_join_zombies(tpool); + + for(e = Curl_llist_head(&tpool->slots); e && nthreads; e = n) { + struct thrdslot *tslot = Curl_node_elem(e); + n = Curl_node_next(e); + if(tslot->idle) { + Curl_cond_signal(&tslot->await); + --nthreads; + } + else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) { + /* starting thread, queries for work soon. */ + --nthreads; + } + } + + while(nthreads && !result && + Curl_llist_count(&tpool->slots) < tpool->max_threads) { + result = thrdslot_start(tpool); + if(result) + break; + --nthreads; + } + + return result; +} + +CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + CURLcode result = CURLE_OK; + size_t running; + + if(!max_threads || (min_threads > max_threads)) + return CURLE_BAD_FUNCTION_ARGUMENT; + + Curl_mutex_acquire(&tpool->lock); + tpool->min_threads = min_threads; + tpool->max_threads = max_threads; + tpool->idle_time_ms = idle_time_ms; + running = Curl_llist_count(&tpool->slots); + if(tpool->min_threads > running) { + result = thrdpool_signal(tpool, tpool->min_threads - (uint32_t)running); + } + Curl_mutex_release(&tpool->lock); + + return result; +} + +CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, + const char *name, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdpool_take_item_cb *fn_take, + Curl_thrdpool_process_item_cb *fn_process, + Curl_thrdpool_return_item_cb *fn_return, + void *user_data) +{ + struct curl_thrdpool *tpool; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tpool = curlx_calloc(1, sizeof(*tpool)); + if(!tpool) + goto out; + tpool->refcount = 1; + + Curl_mutex_init(&tpool->lock); + Curl_cond_init(&tpool->await); + Curl_llist_init(&tpool->slots, NULL); + Curl_llist_init(&tpool->zombies, NULL); + tpool->fn_take = fn_take; + tpool->fn_process = fn_process; + tpool->fn_return = fn_return; + tpool->fn_user_data = user_data; + + tpool->name = curlx_strdup(name); + if(!tpool->name) + goto out; + + result = Curl_thrdpool_set_props(tpool, min_threads, max_threads, + idle_time_ms); + +out: + if(result && tpool) { + tpool->aborted = TRUE; + thrdpool_unlink(tpool, FALSE); + tpool = NULL; + } + *ptpool = tpool; + return result; +} + +void Curl_thrdpool_destroy(struct curl_thrdpool *tpool, bool join) +{ + Curl_mutex_acquire(&tpool->lock); + + tpool->aborted = TRUE; + + while(join && Curl_llist_count(&tpool->slots)) { + thrdpool_wake_all(tpool); + Curl_cond_wait(&tpool->await, &tpool->lock); + } + + thrdpool_join_zombies(tpool); + + /* detach all still running threads */ + if(Curl_llist_count(&tpool->slots)) { + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + if(tslot->thread != curl_thread_t_null) + Curl_thread_destroy(&tslot->thread); + } + tpool->detached = TRUE; + } + + if(!thrdpool_unlink(tpool, TRUE)) { + /* tpool not destroyed */ + Curl_mutex_release(&tpool->lock); + } +} + +CURLcode Curl_thrdpool_signal(struct curl_thrdpool *tpool, uint32_t nthreads) +{ + CURLcode result; + + Curl_mutex_acquire(&tpool->lock); + result = thrdpool_signal(tpool, nthreads); + Curl_mutex_release(&tpool->lock); + return result; +} + +static bool thrdpool_all_idle(struct curl_thrdpool *tpool) +{ + struct Curl_llist_node *e; + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + if(!tslot->idle) + return FALSE; + } + return TRUE; +} + +CURLcode Curl_thrdpool_await_idle(struct curl_thrdpool *tpool, + uint32_t timeout_ms) +{ + CURLcode result = CURLE_OK; + struct curltime end = { 0 }; + + Curl_mutex_acquire(&tpool->lock); + DEBUGASSERT(!tpool->aborted); + if(tpool->aborted) { + result = CURLE_FAILED_INIT; + goto out; + } + + while(!thrdpool_all_idle(tpool)) { + if(timeout_ms) { + timediff_t remain_ms; + CURLcode r; + + if(!end.tv_sec && !end.tv_usec) { + end = curlx_now(); + end.tv_sec += (time_t)(timeout_ms / 1000); + end.tv_usec += (int)(timeout_ms % 1000) * 1000; + if(end.tv_usec >= 1000000) { + end.tv_sec++; + end.tv_usec -= 1000000; + } + } + remain_ms = curlx_timediff_ms(curlx_now(), end); + if(remain_ms <= 0) + r = CURLE_OPERATION_TIMEDOUT; + else + r = Curl_cond_timedwait(&tpool->await, &tpool->lock, + (uint32_t)remain_ms); + if(r == CURLE_OPERATION_TIMEDOUT) { + result = r; + break; + } + } + else { + Curl_cond_wait(&tpool->await, &tpool->lock); + } + } + +out: + thrdpool_join_zombies(tpool); + Curl_mutex_release(&tpool->lock); + return result; +} + +#ifdef CURLVERBOSE +void Curl_thrdpool_trace(struct curl_thrdpool *tpool, + struct Curl_easy *data) +{ + struct curl_trc_feat *feat = &Curl_trc_feat_threads; + if(Curl_trc_ft_is_verbose(data, feat)) { + struct Curl_llist_node *e; + struct curltime now = curlx_now(); + + Curl_mutex_acquire(&tpool->lock); + if(!Curl_llist_count(&tpool->slots)) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] no threads running", + tpool->name); + } + for(e = Curl_llist_head(&tpool->slots); e; e = Curl_node_next(e)) { + struct thrdslot *tslot = Curl_node_elem(e); + timediff_t elapsed_ms = curlx_ptimediff_ms(&now, &tslot->starttime); + if(!tslot->running) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: not running", + tpool->name, tslot->id); + } + else if(!tslot->starttime.tv_sec && !tslot->starttime.tv_usec) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: starting...", + tpool->name, tslot->id); + } + else if(tslot->idle) { + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: idle for %" + FMT_TIMEDIFF_T "ms", + tpool->name, tslot->id, elapsed_ms); + } + else { + timediff_t remain_ms = tslot->work_timeout_ms ? + (tslot->work_timeout_ms - elapsed_ms) : 0; + Curl_trc_feat_infof(data, feat, "[TPOOL-%s] [%u]: busy %" + FMT_TIMEDIFF_T "ms, timeout in %" FMT_TIMEDIFF_T + "ms: %s", + tpool->name, tslot->id, elapsed_ms, remain_ms, + tslot->work_description); + } + } + Curl_mutex_release(&tpool->lock); + } +} +#endif + +#endif /* USE_THREADS */
diff --git a/Utilities/cmcurl/lib/thrdpool.h b/Utilities/cmcurl/lib/thrdpool.h new file mode 100644 index 0000000..cc81fb6 --- /dev/null +++ b/Utilities/cmcurl/lib/thrdpool.h
@@ -0,0 +1,106 @@ +#ifndef HEADER_CURL_THRDPOOL_H +#define HEADER_CURL_THRDPOOL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "curlx/timediff.h" + +#ifdef USE_THREADS + +struct curl_thrdpool; +struct Curl_easy; + +/* Invoked under thread pool lock to get an "item" to work on. Must + * return NULL if there is nothing to do. + * Caller might return a descriptive string about the "item", where + * available. The string needs to have the same lifetime as the + * item itself. */ +typedef void *Curl_thrdpool_take_item_cb(void *user_data, + const char **pdescription, + timediff_t *ptimeout_ms); + +/* Invoked outside thread pool lock to process the item taken. */ +typedef void Curl_thrdpool_process_item_cb(void *item); + +/* Invoked under thread pool lock to return a processed item back + * to the producer. + * If the thread pool has been destroyed, `user_data` will be NULL + * and the callback is responsible to release all `item` resources. */ +typedef void Curl_thrdpool_return_item_cb(void *item, void *user_data); + +/* Create a new thread pool. + * @param name name of pool for tracing purposes + * @param min_threads minimum number of threads to have always running + * @param max_threads maximum number of threads running, ever. + * @param idle_time_ms maximum time a thread should wait for tasks to + * process before shutting down (unless the pool is + * already at minimum thread count), use 0 for + * infinite wait. + * @param fn_take take the next item to process + * @param fn_process process the item taken + * @param fn_return return the processed item + * @param user_data parameter passed to take/return callbacks + */ +CURLcode Curl_thrdpool_create(struct curl_thrdpool **ptpool, + const char *name, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdpool_take_item_cb *fn_take, + Curl_thrdpool_process_item_cb *fn_process, + Curl_thrdpool_return_item_cb *fn_return, + void *user_data); + +/* Destroy the thread pool, release its resources. + * With `join` being TRUE, the call will wait for all threads to finish + * processing before returning. On FALSE, it will detach all threads + * running. Ongoing item processing will continue to run and + * `fn_return` will be invoked with NULL user_data before the thread exits. + */ +void Curl_thrdpool_destroy(struct curl_thrdpool *tpool, bool join); + +/* Signal the pool to wake up `nthreads` idle worker threads, possible + * creating new threads up to the max limit. The number should reflect + * the items that can actually be taken for processing right away, e.g. + * the producers "queue" length of outstanding items. + */ +CURLcode Curl_thrdpool_signal(struct curl_thrdpool *tpool, uint32_t nthreads); + +CURLcode Curl_thrdpool_await_idle(struct curl_thrdpool *tpool, + uint32_t timeout_ms); + +/* Change the properties of a threadpool. */ +CURLcode Curl_thrdpool_set_props(struct curl_thrdpool *tpool, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#ifdef CURLVERBOSE +void Curl_thrdpool_trace(struct curl_thrdpool *tpool, + struct Curl_easy *data); +#endif + +#endif /* USE_THREADS */ + +#endif /* HEADER_CURL_THRDPOOL_H */
diff --git a/Utilities/cmcurl/lib/thrdqueue.c b/Utilities/cmcurl/lib/thrdqueue.c new file mode 100644 index 0000000..1521ccf --- /dev/null +++ b/Utilities/cmcurl/lib/thrdqueue.c
@@ -0,0 +1,411 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_THREADS + +#include "llist.h" +#include "curl_threads.h" +#include "thrdpool.h" +#include "thrdqueue.h" +#include "curlx/timeval.h" +#ifdef CURLVERBOSE +#include "curl_trc.h" +#include "urldata.h" +#endif + + +struct curl_thrdq { + char *name; + curl_mutex_t lock; + curl_cond_t await; + struct Curl_llist sendq; + struct Curl_llist recvq; + struct curl_thrdpool *tpool; + Curl_thrdq_item_free_cb *fn_free; + Curl_thrdq_item_process_cb *fn_process; + Curl_thrdq_ev_cb *fn_event; + void *fn_user_data; + uint32_t send_max_len; + BIT(aborted); +}; + +struct thrdq_item { + struct Curl_llist_node node; + Curl_thrdq_item_free_cb *fn_free; + Curl_thrdq_item_process_cb *fn_process; + void *item; + struct curltime start; + timediff_t timeout_ms; + const char *description; +}; + +static struct thrdq_item *thrdq_item_create(struct curl_thrdq *tqueue, + void *item, + const char *description, + timediff_t timeout_ms) +{ + struct thrdq_item *qitem; + + qitem = curlx_calloc(1, sizeof(*qitem)); + if(!qitem) + return NULL; + qitem->item = item; + qitem->description = description; + qitem->fn_free = tqueue->fn_free; + qitem->fn_process = tqueue->fn_process; + if(timeout_ms) { + qitem->start = curlx_now(); + qitem->timeout_ms = timeout_ms; + } + return qitem; +} + +static void thrdq_item_destroy(struct thrdq_item *qitem) +{ + if(qitem->item) + qitem->fn_free(qitem->item); + curlx_free(qitem); +} + +static void thrdq_item_list_dtor(void *user_data, void *elem) +{ + (void)user_data; + thrdq_item_destroy(elem); +} + +static void *thrdq_tpool_take(void *user_data, const char **pdescription, + timediff_t *ptimeout_ms) +{ + struct curl_thrdq *tqueue = user_data; + struct thrdq_item *qitem = NULL; + struct Curl_llist_node *e; + Curl_thrdq_ev_cb *fn_event = NULL; + void *fn_user_data = NULL; + + Curl_mutex_acquire(&tqueue->lock); + *pdescription = NULL; + *ptimeout_ms = 0; + if(!tqueue->aborted) { + e = Curl_llist_head(&tqueue->sendq); + if(e) { + struct curltime now = curlx_now(); + timediff_t timeout_ms; + while(e) { + qitem = Curl_node_take_elem(e); + timeout_ms = (!qitem->timeout_ms) ? 0 : + (qitem->timeout_ms - curlx_ptimediff_ms(&now, &qitem->start)); + if(timeout_ms < 0) { + /* timed out while queued, place on receive queue */ + Curl_llist_append(&tqueue->recvq, qitem, &qitem->node); + fn_event = tqueue->fn_event; + fn_user_data = tqueue->fn_user_data; + qitem = NULL; + e = Curl_llist_head(&tqueue->sendq); + continue; + } + else { + *pdescription = qitem->description; + *ptimeout_ms = timeout_ms; + break; + } + } + } + } + Curl_mutex_release(&tqueue->lock); + /* avoiding deadlocks */ + if(fn_event) + fn_event(tqueue, CURL_THRDQ_EV_ITEM_DONE, fn_user_data); + return qitem; +} + +static void thrdq_tpool_return(void *item, void *user_data) +{ + struct curl_thrdq *tqueue = user_data; + struct thrdq_item *qitem = item; + Curl_thrdq_ev_cb *fn_event = NULL; + void *fn_user_data = NULL; + + if(!tqueue) { + thrdq_item_destroy(item); + return; + } + + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + thrdq_item_destroy(qitem); + } + else { + DEBUGASSERT(!Curl_node_llist(&qitem->node)); + Curl_llist_append(&tqueue->recvq, qitem, &qitem->node); + fn_event = tqueue->fn_event; + fn_user_data = tqueue->fn_user_data; + } + Curl_mutex_release(&tqueue->lock); + /* avoiding deadlocks */ + if(fn_event) + fn_event(tqueue, CURL_THRDQ_EV_ITEM_DONE, fn_user_data); +} + +static void thrdq_tpool_process(void *item) +{ + struct thrdq_item *qitem = item; + qitem->fn_process(qitem->item); +} + +static void thrdq_unlink(struct curl_thrdq *tqueue, bool locked, bool join) +{ + DEBUGASSERT(tqueue->aborted); + if(tqueue->tpool) { + if(locked) + Curl_mutex_release(&tqueue->lock); + Curl_thrdpool_destroy(tqueue->tpool, join); + tqueue->tpool = NULL; + if(locked) + Curl_mutex_acquire(&tqueue->lock); + } + + Curl_llist_destroy(&tqueue->sendq, NULL); + Curl_llist_destroy(&tqueue->recvq, NULL); + curlx_free(tqueue->name); + Curl_cond_destroy(&tqueue->await); + if(locked) + Curl_mutex_release(&tqueue->lock); + Curl_mutex_destroy(&tqueue->lock); + curlx_free(tqueue); +} + +CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, + const char *name, + uint32_t max_len, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdq_item_free_cb *fn_free, + Curl_thrdq_item_process_cb *fn_process, + Curl_thrdq_ev_cb *fn_event, + void *user_data) +{ + struct curl_thrdq *tqueue; + CURLcode result = CURLE_OUT_OF_MEMORY; + + tqueue = curlx_calloc(1, sizeof(*tqueue)); + if(!tqueue) + goto out; + + Curl_mutex_init(&tqueue->lock); + Curl_cond_init(&tqueue->await); + Curl_llist_init(&tqueue->sendq, thrdq_item_list_dtor); + Curl_llist_init(&tqueue->recvq, thrdq_item_list_dtor); + tqueue->fn_free = fn_free; + tqueue->fn_process = fn_process; + tqueue->fn_event = fn_event; + tqueue->fn_user_data = user_data; + tqueue->send_max_len = max_len; + + tqueue->name = curlx_strdup(name); + if(!tqueue->name) + goto out; + + result = Curl_thrdpool_create(&tqueue->tpool, name, + min_threads, max_threads, idle_time_ms, + thrdq_tpool_take, + thrdq_tpool_process, + thrdq_tpool_return, + tqueue); + +out: + if(result && tqueue) { + tqueue->aborted = TRUE; + thrdq_unlink(tqueue, FALSE, TRUE); + tqueue = NULL; + } + *ptqueue = tqueue; + return result; +} + +void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join) +{ + Curl_mutex_acquire(&tqueue->lock); + DEBUGASSERT(!tqueue->aborted); + tqueue->aborted = TRUE; + thrdq_unlink(tqueue, TRUE, join); +} + +CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, + const char *description, timediff_t timeout_ms) +{ + CURLcode result = CURLE_AGAIN; + size_t signals = 0; + + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + result = CURLE_SEND_ERROR; + goto out; + } + if(timeout_ms < 0) { + result = CURLE_OPERATION_TIMEDOUT; + goto out; + } + + if(!tqueue->send_max_len || + (Curl_llist_count(&tqueue->sendq) < tqueue->send_max_len)) { + struct thrdq_item *qitem = thrdq_item_create(tqueue, item, description, + timeout_ms); + if(!qitem) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + item = NULL; + Curl_llist_append(&tqueue->sendq, qitem, &qitem->node); + signals = Curl_llist_count(&tqueue->sendq); + result = CURLE_OK; + } + +out: + Curl_mutex_release(&tqueue->lock); + /* Signal thread pool unlocked to avoid deadlocks. Since we added + * item to the queue already, it might have been taken for processing + * already. Any error in signalling the pool cannot be reported to + * the caller since it needs to give up ownership of item. */ + if(!result && signals) + (void)Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals); + return result; +} + +CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem) +{ + CURLcode result = CURLE_AGAIN; + struct Curl_llist_node *e; + + *pitem = NULL; + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + result = CURLE_RECV_ERROR; + goto out; + } + + e = Curl_llist_head(&tqueue->recvq); + if(e) { + struct thrdq_item *qitem = Curl_node_take_elem(e); + *pitem = qitem->item; + qitem->item = NULL; + thrdq_item_destroy(qitem); + result = CURLE_OK; + } +out: + Curl_mutex_release(&tqueue->lock); + return result; +} + +static void thrdq_llist_clean_matches(struct Curl_llist *llist, + Curl_thrdq_item_match_cb *fn_match, + void *match_data) +{ + struct Curl_llist_node *e, *n; + struct thrdq_item *qitem; + + for(e = Curl_llist_head(llist); e; e = n) { + n = Curl_node_next(e); + qitem = Curl_node_elem(e); + if(fn_match(qitem->item, match_data)) + Curl_node_remove(e); + } +} + +void Curl_thrdq_clear(struct curl_thrdq *tqueue, + Curl_thrdq_item_match_cb *fn_match, + void *match_data) +{ + Curl_mutex_acquire(&tqueue->lock); + if(tqueue->aborted) { + DEBUGASSERT(0); + goto out; + } + thrdq_llist_clean_matches(&tqueue->sendq, fn_match, match_data); + thrdq_llist_clean_matches(&tqueue->recvq, fn_match, match_data); +out: + Curl_mutex_release(&tqueue->lock); +} + +CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms) +{ + return Curl_thrdpool_await_idle(tqueue->tpool, timeout_ms); +} + +CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, + uint32_t max_len, + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms) +{ + CURLcode result; + size_t signals; + + Curl_mutex_acquire(&tqueue->lock); + tqueue->send_max_len = max_len; + signals = Curl_llist_count(&tqueue->sendq); + Curl_mutex_release(&tqueue->lock); + + result = Curl_thrdpool_set_props(tqueue->tpool, min_threads, + max_threads, idle_time_ms); + if(!result && signals) + result = Curl_thrdpool_signal(tqueue->tpool, (uint32_t)signals); + return result; +} + +#ifdef CURLVERBOSE +void Curl_thrdq_trace(struct curl_thrdq *tqueue, + struct Curl_easy *data) +{ + struct curl_trc_feat *feat = &Curl_trc_feat_threads; + if(Curl_trc_ft_is_verbose(data, feat)) { + struct Curl_llist_node *e; + struct thrdq_item *qitem; + + Curl_thrdpool_trace(tqueue->tpool, data); + Curl_mutex_acquire(&tqueue->lock); + if(!Curl_llist_count(&tqueue->sendq) && + !Curl_llist_count(&tqueue->recvq)) { + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] empty", tqueue->name); + } + for(e = Curl_llist_head(&tqueue->sendq); e; e = Curl_node_next(e)) { + qitem = Curl_node_elem(e); + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] in: %s", + tqueue->name, qitem->description); + } + for(e = Curl_llist_head(&tqueue->recvq); e; e = Curl_node_next(e)) { + qitem = Curl_node_elem(e); + Curl_trc_feat_infof(data, feat, "[TQUEUE-%s] out: %s", + tqueue->name, qitem->description); + } + Curl_mutex_release(&tqueue->lock); + } +} +#endif + +#endif /* USE_THREADS */
diff --git a/Utilities/cmcurl/lib/thrdqueue.h b/Utilities/cmcurl/lib/thrdqueue.h new file mode 100644 index 0000000..d267f5d --- /dev/null +++ b/Utilities/cmcurl/lib/thrdqueue.h
@@ -0,0 +1,118 @@ +#ifndef HEADER_CURL_THRDQUEUE_H +#define HEADER_CURL_THRDQUEUE_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "curl_setup.h" +#include "curlx/timediff.h" + +#ifdef USE_THREADS + +struct Curl_easy; +struct curl_thrdq; + +typedef enum { + CURL_THRDQ_EV_ITEM_DONE /* an item has been processed and is ready */ +} Curl_thrdq_event; + +/* Notification callback when "events" happen in the queue. May be + * call from any thread, queue is not locked. */ +typedef void Curl_thrdq_ev_cb(const struct curl_thrdq *tqueue, + Curl_thrdq_event ev, + void *user_data); + +/* Process a queued item. Maybe call from any thread. Queue is + * not locked. */ +typedef void Curl_thrdq_item_process_cb(void *item); + +/* Free an item. May be called from any thread at any time for an + * item that is in the queue (either before or after processing). */ +typedef void Curl_thrdq_item_free_cb(void *item); + +/* Create a new queue processing "items" by a thread pool. + */ +CURLcode Curl_thrdq_create(struct curl_thrdq **ptqueue, + const char *name, + uint32_t max_len, /* 0 for unlimited */ + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms, + Curl_thrdq_item_free_cb *fn_free, + Curl_thrdq_item_process_cb *fn_process, + Curl_thrdq_ev_cb *fn_event, /* optional */ + void *user_data); + +/* Destroy the queue, free all queued items unprocessed and destroy + * the thread pool used. + * @param join TRUE when thread pool shall be joined. FALSE for + * detaching any running threads. + */ +void Curl_thrdq_destroy(struct curl_thrdq *tqueue, bool join); + +/* Send "item" onto the queue. The caller needs to clear any reference + * to "item" on success, e.g. the queue takes ownership. + * `description` is an optional string describing the item for tracing + * purposes. It needs to have the same lifetime as `item`. + * Returns CURLE_AGAIN when the queue has already been full. + * + * With`timeout_ms` != 0, items that get stuck that long in the send + * queue are removed and added to the receive queue right away. + */ +CURLcode Curl_thrdq_send(struct curl_thrdq *tqueue, void *item, + const char *description, timediff_t timeout_ms); + +/* Receive the oldest, processed item from the queue again, if there is one. + * The caller takes ownership of the item received, e.g. the queue + * relinquishes all references to item. + * Returns CURLE_AGAIN when there is no processed item, setting `pitem` + * to NULL. + */ +CURLcode Curl_thrdq_recv(struct curl_thrdq *tqueue, void **pitem); + +/* Return TRUE if the passed "item" matches. */ +typedef bool Curl_thrdq_item_match_cb(void *item, void *match_data); + +/* Clear all scheduled/processed items that match from the queue. This + * will *not* be able to clear items that are being processed. + */ +void Curl_thrdq_clear(struct curl_thrdq *tqueue, + Curl_thrdq_item_match_cb *fn_match, + void *match_data); + +CURLcode Curl_thrdq_await_done(struct curl_thrdq *tqueue, + uint32_t timeout_ms); + +CURLcode Curl_thrdq_set_props(struct curl_thrdq *tqueue, + uint32_t max_len, /* 0 for unlimited */ + uint32_t min_threads, + uint32_t max_threads, + uint32_t idle_time_ms); + +#ifdef CURLVERBOSE +void Curl_thrdq_trace(struct curl_thrdq *tqueue, + struct Curl_easy *data); +#endif + +#endif /* USE_THREADS */ + +#endif /* HEADER_CURL_THRDQUEUE_H */
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c index 6dd2f52..fd1a903 100644 --- a/Utilities/cmcurl/lib/transfer.c +++ b/Utilities/cmcurl/lib/transfer.c
@@ -59,6 +59,7 @@ #include "hostip.h" #include "cfilters.h" #include "cw-out.h" +#include "dnscache.h" #include "transfer.h" #include "sendf.h" #include "curl_trc.h" @@ -303,15 +304,15 @@ * we should read the EOS. Which may arrive as meta data after * the bytes. Not taking it in might lead to RST of streams. */ if((!is_multiplex && data->req.download_done) || is_eos) { - data->req.keepon &= ~KEEP_RECV; + CURL_REQ_CLEAR_RECV(data); } /* if we stopped receiving, leave the loop */ - if(!(k->keepon & KEEP_RECV)) + if(!CURL_REQ_WANT_RECV(data)) break; } while(maxloops--); - if(!is_eos && !rate_limited && CURL_WANT_RECV(data) && + if(!is_eos && !rate_limited && CURL_REQ_WANT_RECV(data) && (!rcvd_eagain || data_pending(data, rcvd_eagain))) { /* Did not read until EAGAIN/EOS or there is still data pending * in buffers. Mark as read-again via simulated SELECT results. */ @@ -319,7 +320,7 @@ CURL_TRC_M(data, "sendrecv_dl() no EAGAIN/pending data, mark as dirty"); } - if(((k->keepon & (KEEP_RECV | KEEP_SEND)) == KEEP_SEND) && + if(!CURL_REQ_WANT_RECV(data) && CURL_REQ_WANT_SEND(data) && (conn->bits.close || is_multiplex)) { /* When we have read the entire thing and the close bit is set, the server may now close the connection. If there is now any kind of sending going @@ -340,9 +341,7 @@ */ static CURLcode sendrecv_ul(struct Curl_easy *data) { - /* We should not get here when the sending is already done. It - * probably means that someone set `data-req.keepon |= KEEP_SEND` - * when it should not. */ + /* We should not get here when the sending is already done. */ DEBUGASSERT(!Curl_req_done_sending(data)); if(!Curl_req_done_sending(data)) @@ -366,7 +365,7 @@ /* We go ahead and do a read if we have a readable socket or if the stream was rewound (in which case we have data in a buffer) */ - if(k->keepon & KEEP_RECV) { + if(CURL_REQ_WANT_RECV(data)) { result = sendrecv_dl(data, k); if(result || data->req.done) goto out; @@ -383,7 +382,7 @@ if(result) goto out; - if(k->keepon) { + if(CURL_REQ_WANT_IO(data)) { if(Curl_timeleft_ms(data) < 0) { if(k->size != -1) { failf(data, "Operation timed out after %" FMT_TIMEDIFF_T @@ -419,7 +418,7 @@ } /* If there is nothing more to send/recv, the request is done */ - if((k->keepon & (KEEP_RECV | KEEP_SEND)) == 0) + if(!CURL_REQ_WANT_IO(data)) data->req.done = TRUE; result = Curl_pgrsUpdate(data); @@ -440,6 +439,40 @@ } /* + * Restore the user credentials to those set in options. + */ +CURLcode Curl_reset_userpwd(struct Curl_easy *data) +{ + CURLcode result; + if(data->set.str[STRING_USERNAME] || data->set.str[STRING_PASSWORD]) + data->state.creds_from = CREDS_OPTION; + result = Curl_setstropt(&data->state.aptr.user, + data->set.str[STRING_USERNAME]); + if(!result) + result = Curl_setstropt(&data->state.aptr.passwd, + data->set.str[STRING_PASSWORD]); + return result; +} + +/* + * Restore the proxy credentials to those set in options. + */ +CURLcode Curl_reset_proxypwd(struct Curl_easy *data) +{ +#ifndef CURL_DISABLE_PROXY + CURLcode result = Curl_setstropt(&data->state.aptr.proxyuser, + data->set.str[STRING_PROXYUSERNAME]); + if(!result) + result = Curl_setstropt(&data->state.aptr.proxypasswd, + data->set.str[STRING_PROXYPASSWORD]); + return result; +#else + (void)data; + return CURLE_OK; +#endif +} + +/* * Curl_pretransfer() is called immediately before a transfer starts, and only * once for one transfer no matter if it has redirects or do multi-pass * authentication etc. @@ -470,6 +503,8 @@ uc = curl_url_get(data->set.uh, CURLUPART_URL, &data->set.str[STRING_SET_URL], 0); if(uc) { + /* clear the pointer to not point to freed memory anymore */ + Curl_bufref_set(&data->state.url, NULL, 0, NULL); failf(data, "No URL set"); return CURLE_URL_MALFORMAT; } @@ -492,6 +527,7 @@ data->state.requests = 0; data->state.followlocation = 0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ + data->state.http_ignorecustom = FALSE; /* use custom HTTP method */ data->state.errorbuf = FALSE; /* no error has occurred */ #ifndef CURL_DISABLE_HTTP Curl_http_neg_init(data, &data->state.http_neg); @@ -499,8 +535,13 @@ data->state.authproblem = FALSE; data->state.authhost.want = data->set.httpauth; data->state.authproxy.want = data->set.proxyauth; - Curl_safefree(data->info.wouldredirect); + curlx_safefree(data->info.wouldredirect); Curl_data_priority_clear_state(data); + if(data->set.http_auto_referer) + Curl_bufref_free(&data->state.referer); + if(data->set.str[STRING_SET_REFERER]) + Curl_bufref_set(&data->state.referer, data->set.str[STRING_SET_REFERER], + 0, NULL); if(data->state.httpreq == HTTPREQ_PUT) data->state.infilesize = data->set.filesize; @@ -563,8 +604,8 @@ if(wc->state < CURLWC_INIT) { if(wc->ftpwc) wc->dtor(wc->ftpwc); - Curl_safefree(wc->pattern); - Curl_safefree(wc->path); + curlx_safefree(wc->pattern); + curlx_safefree(wc->path); Curl_wildcard_init(wc); /* init wildcard structures */ } } @@ -584,23 +625,10 @@ return CURLE_OUT_OF_MEMORY; } - if(data->set.str[STRING_USERNAME] || - data->set.str[STRING_PASSWORD]) - data->state.creds_from = CREDS_OPTION; if(!result) - result = Curl_setstropt(&data->state.aptr.user, - data->set.str[STRING_USERNAME]); + result = Curl_reset_userpwd(data); if(!result) - result = Curl_setstropt(&data->state.aptr.passwd, - data->set.str[STRING_PASSWORD]); -#ifndef CURL_DISABLE_PROXY - if(!result) - result = Curl_setstropt(&data->state.aptr.proxyuser, - data->set.str[STRING_PROXYUSERNAME]); - if(!result) - result = Curl_setstropt(&data->state.aptr.proxypasswd, - data->set.str[STRING_PROXYPASSWORD]); -#endif + result = Curl_reset_proxypwd(data); data->req.headerbytecount = 0; Curl_headers_cleanup(data); @@ -710,14 +738,11 @@ /* we want header and/or body, if neither then do not do this! */ if(conn->scheme->run->write_resp_hd || !data->req.no_body) { - if(conn->recv_idx != -1) - k->keepon |= KEEP_RECV; - + CURL_REQ_SET_RECV(data); if(conn->send_idx != -1) - k->keepon |= KEEP_SEND; + CURL_REQ_SET_SEND(data); } - CURL_TRC_M(data, "xfer_setup: recv_idx=%d, send_idx=%d", conn->recv_idx, conn->send_idx); } @@ -799,7 +824,7 @@ const char *hd0, size_t hdlen, bool is_eos) { if(data->conn->scheme->run->write_resp_hd) { - DEBUGASSERT(!hd0[hdlen]); /* null terminated */ + DEBUGASSERT(!hd0[hdlen]); /* null-terminated */ /* protocol handlers offering this function take full responsibility * for writing all received download data to the client. */ return data->conn->scheme->run->write_resp_hd(data, hd0, hdlen, is_eos); @@ -868,8 +893,8 @@ bool Curl_xfer_is_blocked(struct Curl_easy *data) { - bool want_send = (data->req.keepon & KEEP_SEND); - bool want_recv = (data->req.keepon & KEEP_RECV); + bool want_send = CURL_REQ_WANT_SEND(data); + bool want_recv = CURL_REQ_WANT_RECV(data); if(!want_send) return want_recv && Curl_xfer_recv_is_paused(data); else if(!want_recv) @@ -908,3 +933,25 @@ Curl_pgrsRecvPause(data, enable); return result; } + +bool Curl_xfer_is_secure(struct Curl_easy *data) +{ + const struct Curl_scheme *scheme = NULL; + + if(data->conn) { + scheme = data->conn->scheme; + /* if we are connected, but not use SSL, the transfer is not secure. + * This covers an insecure http:// proxy that is not tunneling. + * We enforce tunneling for such cases, but better be sure here. */ + if(Curl_conn_is_connected(data->conn, FIRSTSOCKET) && + !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) + return FALSE; + } + else if(data->info.conn_scheme) { /* was connected once */ + scheme = Curl_get_scheme(data->info.conn_scheme); + } + else { /* never connected (yet?) */ + DEBUGASSERT(0); /* not implemented, would need to parse URL */ + } + return scheme ? (scheme->flags & PROTOPT_SSL) : FALSE; +}
diff --git a/Utilities/cmcurl/lib/transfer.h b/Utilities/cmcurl/lib/transfer.h index 05a5f89..b29e70b 100644 --- a/Utilities/cmcurl/lib/transfer.h +++ b/Utilities/cmcurl/lib/transfer.h
@@ -31,6 +31,8 @@ void Curl_init_CONNECT(struct Curl_easy *data); +CURLcode Curl_reset_userpwd(struct Curl_easy *data); +CURLcode Curl_reset_proxypwd(struct Curl_easy *data); CURLcode Curl_pretransfer(struct Curl_easy *data); CURLcode Curl_sendrecv(struct Curl_easy *data); @@ -39,8 +41,8 @@ /** * Write the transfer raw response bytes, as received from the connection. - * Will handle all passed bytes or return an error. By default, this will - * write the bytes as BODY to the client. Protocols may provide a + * Handle all passed bytes or return an error. By default, this writes + * the bytes as BODY to the client. Protocols may provide a * "write_resp" callback in their handler to add specific treatment. E.g. * HTTP parses response headers and passes them differently to the client. * @param data the transfer @@ -112,7 +114,7 @@ /** * Send data on the socket/connection filter designated * for transfer's outgoing data. - * Will return CURLE_OK on blocking with (*pnwritten == 0). + * Return CURLE_OK on blocking with (*pnwritten == 0). */ CURLcode Curl_xfer_send(struct Curl_easy *data, const void *buf, size_t blen, bool eos, @@ -121,7 +123,7 @@ /** * Receive data on the socket/connection filter designated * for transfer's incoming data. - * Will return CURLE_AGAIN on blocking with (*pnrcvd == 0). + * Return CURLE_AGAIN on blocking with (*pnrcvd == 0). */ CURLcode Curl_xfer_recv(struct Curl_easy *data, char *buf, size_t blen, @@ -143,4 +145,8 @@ CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable); CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable); +/* TRUE if the transfer is secure (e.g. TLS) from libcurl to the + * URL's host. */ +bool Curl_xfer_is_secure(struct Curl_easy *data); + #endif /* HEADER_CURL_TRANSFER_H */
diff --git a/Utilities/cmcurl/lib/uint-bset.c b/Utilities/cmcurl/lib/uint-bset.c index 5efdbea..5469174 100644 --- a/Utilities/cmcurl/lib/uint-bset.c +++ b/Utilities/cmcurl/lib/uint-bset.c
@@ -68,7 +68,9 @@ } #ifdef UNITTESTS -UNITTEST uint32_t Curl_uint32_bset_capacity(struct uint32_bset *bset) +/* @unittest 3211 */ +UNITTEST uint32_t uint32_bset_capacity(struct uint32_bset *bset); +UNITTEST uint32_t uint32_bset_capacity(struct uint32_bset *bset) { return bset->nslots * 64; }
diff --git a/Utilities/cmcurl/lib/uint-bset.h b/Utilities/cmcurl/lib/uint-bset.h index 7455c42..ce0450c 100644 --- a/Utilities/cmcurl/lib/uint-bset.h +++ b/Utilities/cmcurl/lib/uint-bset.h
@@ -56,9 +56,6 @@ /* Destroy the bitset, freeing all resources. */ void Curl_uint32_bset_destroy(struct uint32_bset *bset); -/* Get the bitset capacity, e.g. can hold numbers from 0 to capacity - 1. */ -uint32_t Curl_uint32_bset_capacity(struct uint32_bset *bset); - /* Get the cardinality of the bitset, e.g. numbers present in the set. */ uint32_t Curl_uint32_bset_count(struct uint32_bset *bset);
diff --git a/Utilities/cmcurl/lib/uint-hash.c b/Utilities/cmcurl/lib/uint-hash.c index 2377840..c37448f 100644 --- a/Utilities/cmcurl/lib/uint-hash.c +++ b/Utilities/cmcurl/lib/uint-hash.c
@@ -181,7 +181,9 @@ return NULL; } -static void uint_hash_clear(struct uint_hash *h) +/* @unittest 1616 */ +UNITTEST void uint_hash_clear(struct uint_hash *h); +UNITTEST void uint_hash_clear(struct uint_hash *h) { if(h && h->table) { struct uint_hash_entry *he, **he_anchor; @@ -198,19 +200,12 @@ } } -#ifdef UNITTESTS -UNITTEST void Curl_uint32_hash_clear(struct uint_hash *h) -{ - uint_hash_clear(h); -} -#endif - void Curl_uint32_hash_destroy(struct uint_hash *h) { DEBUGASSERT(h->init == CURL_UINT32_HASHINIT); if(h->table) { uint_hash_clear(h); - Curl_safefree(h->table); + curlx_safefree(h->table); } DEBUGASSERT(h->size == 0); h->slots = 0;
diff --git a/Utilities/cmcurl/lib/uint-hash.h b/Utilities/cmcurl/lib/uint-hash.h index 56d7104..3cbcf8f 100644 --- a/Utilities/cmcurl/lib/uint-hash.h +++ b/Utilities/cmcurl/lib/uint-hash.h
@@ -44,8 +44,6 @@ uint32_t slots, Curl_uint32_hash_dtor *dtor); void Curl_uint32_hash_destroy(struct uint_hash *h); -void Curl_uint32_hash_clear(struct uint_hash *h); - bool Curl_uint32_hash_set(struct uint_hash *h, uint32_t id, void *value); bool Curl_uint32_hash_remove(struct uint_hash *h, uint32_t id); void *Curl_uint32_hash_get(struct uint_hash *h, uint32_t id);
diff --git a/Utilities/cmcurl/lib/uint-spbset.c b/Utilities/cmcurl/lib/uint-spbset.c index 58dcbda..3daa2ee 100644 --- a/Utilities/cmcurl/lib/uint-spbset.c +++ b/Utilities/cmcurl/lib/uint-spbset.c
@@ -30,9 +30,6 @@ #define CURL_UINT32_SPBSET_MAGIC 0x70737362 #endif -/* Clear the bitset, making it empty. */ -UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset); - void Curl_uint32_spbset_init(struct uint32_spbset *bset) { memset(bset, 0, sizeof(*bset)); @@ -41,10 +38,26 @@ #endif } +/* Clear the bitset, making it empty. + + @unittest 3213 + */ +UNITTEST void uint32_spbset_clear(struct uint32_spbset *bset); +UNITTEST void uint32_spbset_clear(struct uint32_spbset *bset) +{ + struct uint32_spbset_chunk *next, *chunk; + + for(chunk = bset->head.next; chunk; chunk = next) { + next = chunk->next; + curlx_free(chunk); + } + memset(&bset->head, 0, sizeof(bset->head)); +} + void Curl_uint32_spbset_destroy(struct uint32_spbset *bset) { DEBUGASSERT(bset->init == CURL_UINT32_SPBSET_MAGIC); - Curl_uint32_spbset_clear(bset); + uint32_spbset_clear(bset); } uint32_t Curl_uint32_spbset_count(struct uint32_spbset *bset) @@ -61,19 +74,8 @@ return n; } -UNITTEST void Curl_uint32_spbset_clear(struct uint32_spbset *bset) -{ - struct uint32_spbset_chunk *next, *chunk; - - for(chunk = bset->head.next; chunk; chunk = next) { - next = chunk->next; - curlx_free(chunk); - } - memset(&bset->head, 0, sizeof(bset->head)); -} - -static struct uint32_spbset_chunk * -uint32_spbset_get_chunk(struct uint32_spbset *bset, uint32_t i, bool grow) +static struct uint32_spbset_chunk *uint32_spbset_get_chunk( + struct uint32_spbset *bset, uint32_t i, bool grow) { struct uint32_spbset_chunk *chunk, **panchor = NULL; uint32_t i_offset = (i & ~CURL_UINT32_SPBSET_CH_MASK);
diff --git a/Utilities/cmcurl/lib/uint-table.c b/Utilities/cmcurl/lib/uint-table.c index b9c1990..addd2c9 100644 --- a/Utilities/cmcurl/lib/uint-table.c +++ b/Utilities/cmcurl/lib/uint-table.c
@@ -29,9 +29,6 @@ #define CURL_UINT32_TBL_MAGIC 0x62757473 #endif -/* Clear the table, making it empty. */ -UNITTEST void Curl_uint32_tbl_clear(struct uint32_tbl *tbl); - void Curl_uint32_tbl_init(struct uint32_tbl *tbl, Curl_uint32_tbl_entry_dtor *entry_dtor) { @@ -82,15 +79,12 @@ return CURLE_OK; } -void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl) -{ - DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); - Curl_uint32_tbl_clear(tbl); - curlx_free(tbl->rows); - memset(tbl, 0, sizeof(*tbl)); -} +/* Clear the table, making it empty. -UNITTEST void Curl_uint32_tbl_clear(struct uint32_tbl *tbl) + @unittest 3212 + */ +UNITTEST void uint32_tbl_clear(struct uint32_tbl *tbl); +UNITTEST void uint32_tbl_clear(struct uint32_tbl *tbl) { DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); uint32_tbl_clear_rows(tbl, 0, tbl->nrows); @@ -98,6 +92,14 @@ tbl->last_key_added = UINT32_MAX; } +void Curl_uint32_tbl_destroy(struct uint32_tbl *tbl) +{ + DEBUGASSERT(tbl->init == CURL_UINT32_TBL_MAGIC); + uint32_tbl_clear(tbl); + curlx_free(tbl->rows); + memset(tbl, 0, sizeof(*tbl)); +} + uint32_t Curl_uint32_tbl_capacity(struct uint32_tbl *tbl) { return tbl->nrows;
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index ec0457b..5fe6803 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c
@@ -100,7 +100,7 @@ #include "curlx/strerr.h" #include "curlx/strparse.h" -/* And now for the protocols */ +/* Now for the protocols */ #include "ftp.h" #include "dict.h" #include "telnet.h" @@ -112,7 +112,6 @@ #include "imap.h" #include "url.h" #include "connect.h" -#include "curl_rtmp.h" #include "gopher.h" #include "mqtt.h" #include "rtsp.h" @@ -133,10 +132,6 @@ # error READBUFFER_SIZE is too small #endif -#if !defined(CURL_DISABLE_PROXY) && defined(USE_UNIX_SOCKETS) -#define UNIX_SOCKET_PREFIX "localhost" -#endif - /* Reject URLs exceeding this length */ #define MAX_URL_LEN 0xffff @@ -165,11 +160,11 @@ enum dupblob j; for(i = (enum dupstring)0; i < STRING_LAST; i++) { - Curl_safefree(data->set.str[i]); + curlx_safefree(data->set.str[i]); } for(j = (enum dupblob)0; j < BLOB_LAST; j++) { - Curl_safefree(data->set.blobs[j]); + curlx_safefree(data->set.blobs[j]); } Curl_bufref_free(&data->state.referer); @@ -177,7 +172,7 @@ #if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->set.mimepostp); - Curl_safefree(data->set.mimepostp); + curlx_safefree(data->set.mimepostp); #endif #ifndef CURL_DISABLE_COOKIES @@ -190,14 +185,14 @@ static void up_free(struct Curl_easy *data) { struct urlpieces *up = &data->state.up; - Curl_safefree(up->scheme); - Curl_safefree(up->hostname); - Curl_safefree(up->port); - Curl_safefree(up->user); - Curl_safefree(up->password); - Curl_safefree(up->options); - Curl_safefree(up->path); - Curl_safefree(up->query); + curlx_safefree(up->scheme); + curlx_safefree(up->hostname); + curlx_safefree(up->port); + curlx_safefree(up->user); + curlx_safefree(up->password); + curlx_safefree(up->options); + curlx_safefree(up->path); + curlx_safefree(up->query); curl_url_cleanup(data->state.uh); data->state.uh = NULL; } @@ -244,9 +239,7 @@ curlx_free(data->state.range); /* release any resolve information this transfer kept */ - Curl_async_destroy(data); - Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */ - Curl_resolv_unlink(data, &data->state.dns[1]); + Curl_resolv_destroy_all(data); data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */ data->magic = 0; /* force a clear AFTER the possibly enforced removal from @@ -259,7 +252,7 @@ /* Close down all open SSL info and sessions */ Curl_ssl_close_all(data); - Curl_safefree(data->state.first_host); + curlx_safefree(data->state.first_host); Curl_ssl_free_certinfo(data); Curl_bufref_free(&data->state.referer); @@ -280,45 +273,38 @@ #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH) Curl_http_auth_cleanup_digest(data); #endif - Curl_safefree(data->state.most_recent_ftp_entrypath); - Curl_safefree(data->info.contenttype); - Curl_safefree(data->info.wouldredirect); + curlx_safefree(data->state.most_recent_ftp_entrypath); + curlx_safefree(data->info.contenttype); + curlx_safefree(data->info.wouldredirect); data_priority_cleanup(data); /* No longer a dirty share, if it exists */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - data->share->dirty--; - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - } + if(Curl_share_easy_unlink(data)) + DEBUGASSERT(0); Curl_hash_destroy(&data->meta_hash); -#ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuserpwd); -#endif - Curl_safefree(data->state.aptr.uagent); - Curl_safefree(data->state.aptr.userpwd); - Curl_safefree(data->state.aptr.accept_encoding); - Curl_safefree(data->state.aptr.rangeline); - Curl_safefree(data->state.aptr.ref); - Curl_safefree(data->state.aptr.host); + curlx_safefree(data->state.aptr.uagent); + curlx_safefree(data->state.aptr.accept_encoding); + curlx_safefree(data->state.aptr.rangeline); + curlx_safefree(data->state.aptr.ref); + curlx_safefree(data->state.aptr.host); #ifndef CURL_DISABLE_COOKIES - Curl_safefree(data->state.aptr.cookiehost); + curlx_safefree(data->req.cookiehost); #endif #ifndef CURL_DISABLE_RTSP - Curl_safefree(data->state.aptr.rtsp_transport); + curlx_safefree(data->state.aptr.rtsp_transport); #endif - Curl_safefree(data->state.aptr.user); - Curl_safefree(data->state.aptr.passwd); + curlx_safefree(data->state.aptr.user); + curlx_safefree(data->state.aptr.passwd); #ifndef CURL_DISABLE_PROXY - Curl_safefree(data->state.aptr.proxyuser); - Curl_safefree(data->state.aptr.proxypasswd); + curlx_safefree(data->state.aptr.proxyuser); + curlx_safefree(data->state.aptr.proxypasswd); #endif #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API) Curl_mime_cleanpart(data->state.formp); - Curl_safefree(data->state.formp); + curlx_safefree(data->state.formp); #endif /* destruct wildcard structures if it is needed */ @@ -400,7 +386,7 @@ #endif set->new_file_perms = 0644; /* Default permissions */ - set->allowed_protocols = (curl_prot_t) CURLPROTO_64ALL; + set->allowed_protocols = (curl_prot_t)CURLPROTO_64ALL; set->redir_protocols = CURLPROTO_REDIR; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) @@ -436,6 +422,7 @@ set->sep_headers = TRUE; /* separated header lists by default */ set->buffer_size = READBUFFER_SIZE; set->upload_buffer_size = UPLOADBUFFER_DEFAULT; + set->upload_flags = CURLULFLAG_SEEN; set->happy_eyeballs_timeout = CURL_HET_DEFAULT; set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ @@ -458,7 +445,7 @@ static void easy_meta_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -531,28 +518,28 @@ #ifndef CURL_DISABLE_PROXY Curl_free_idnconverted_hostname(&conn->http_proxy.host); Curl_free_idnconverted_hostname(&conn->socks_proxy.host); - Curl_safefree(conn->http_proxy.user); - Curl_safefree(conn->socks_proxy.user); - Curl_safefree(conn->http_proxy.passwd); - Curl_safefree(conn->socks_proxy.passwd); - Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ - Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ + curlx_safefree(conn->http_proxy.user); + curlx_safefree(conn->socks_proxy.user); + curlx_safefree(conn->http_proxy.passwd); + curlx_safefree(conn->socks_proxy.passwd); + curlx_safefree(conn->http_proxy.host.rawalloc); /* http proxy name */ + curlx_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name */ #endif - Curl_safefree(conn->user); - Curl_safefree(conn->passwd); - Curl_safefree(conn->sasl_authzid); - Curl_safefree(conn->options); - Curl_safefree(conn->oauth_bearer); - Curl_safefree(conn->host.rawalloc); /* hostname buffer */ - Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ - Curl_safefree(conn->secondaryhostname); - Curl_safefree(conn->localdev); + curlx_safefree(conn->user); + curlx_safefree(conn->passwd); + curlx_safefree(conn->sasl_authzid); + curlx_safefree(conn->options); + curlx_safefree(conn->oauth_bearer); + curlx_safefree(conn->host.rawalloc); /* hostname buffer */ + curlx_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */ + curlx_safefree(conn->secondaryhostname); + curlx_safefree(conn->localdev); Curl_ssl_conn_config_cleanup(conn); #ifdef USE_UNIX_SOCKETS - Curl_safefree(conn->unix_domain_socket); + curlx_safefree(conn->unix_domain_socket); #endif - Curl_safefree(conn->destination); + curlx_safefree(conn->destination); Curl_hash_destroy(&conn->meta_hash); curlx_free(conn); /* free all the connection oriented data */ @@ -650,19 +637,12 @@ /* avoid check if already too old */ dead = TRUE; } - else if(conn->scheme->run->connection_check) { + else if(conn->scheme->run->connection_is_dead) { /* The protocol has a special method for checking the state of the connection. Use it to check if the connection is dead. */ - unsigned int state; - - /* briefly attach the connection to this transfer for the purpose of - checking it */ + /* briefly attach the connection for the check */ Curl_attach_connection(data, conn); - - state = conn->scheme->run->connection_check(data, conn, - CONNCHECK_ISDEAD); - dead = (state & CONNRESULT_DEAD); - /* detach the connection again */ + dead = conn->scheme->run->connection_is_dead(data, conn); Curl_detach_connection(data); } else { @@ -705,18 +685,7 @@ /* briefly attach for action */ Curl_attach_connection(data, conn); - if(conn->scheme->run->connection_check) { - /* Do a protocol-specific keepalive check on the connection. */ - unsigned int rc; - rc = conn->scheme->run->connection_check(data, conn, - CONNCHECK_KEEPALIVE); - if(rc & CONNRESULT_DEAD) - result = CURLE_RECV_ERROR; - } - else { - /* Do the generic action on the FIRSTSOCKET filter chain */ - result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET); - } + result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET); Curl_detach_connection(data); conn->keepalive = *Curl_pgrs_now(data); @@ -745,7 +714,7 @@ BIT(want_proxy_ntlm_http); BIT(want_nego_http); BIT(want_proxy_nego_http); - + BIT(req_tls); /* require TLS use from a clear-text start */ BIT(wait_pipe); BIT(force_reuse); BIT(seen_pending_conn); @@ -757,7 +726,7 @@ struct url_conn_match *m) { /* connect-only or to-be-closed connections will not be reused */ - if(conn->connect_only || conn->bits.close || conn->bits.no_reuse) + if(conn->bits.connect_only || conn->bits.close || conn->bits.no_reuse) return FALSE; /* ip_version must match */ @@ -897,6 +866,9 @@ (get_protocol_family(conn->scheme) != m->needle->scheme->protocol)) return FALSE; } + else if(m->req_tls) + /* a clear-text STARTTLS protocol with required TLS */ + return FALSE; return TRUE; } @@ -1110,11 +1082,17 @@ if(m->want_ntlm_http) { if(Curl_timestrcmp(m->needle->user, conn->user) || Curl_timestrcmp(m->needle->passwd, conn->passwd)) { - /* we prefer a credential match, but this is at least a connection - that can be reused and "upgraded" to NTLM */ - if(conn->http_ntlm_state == NTLMSTATE_NONE) + that can be reused and "upgraded" to NTLM if it does + not have any auth ongoing. */ +#ifdef USE_SPNEGO + if((conn->http_ntlm_state == NTLMSTATE_NONE) + && (conn->http_negotiate_state == GSS_AUTHNONE)) { +#else + if(conn->http_ntlm_state == NTLMSTATE_NONE) { +#endif m->found = conn; + } return FALSE; } } @@ -1285,10 +1263,9 @@ return TRUE; } -static bool url_match_result(bool result, void *userdata) +static bool url_match_result(void *userdata) { struct url_conn_match *match = userdata; - (void)result; if(match->found) { /* Attach it now while still under lock, so the connection does * no longer appear idle and can be reaped. */ @@ -1324,7 +1301,7 @@ bool *waitpipe) { struct url_conn_match match; - bool result; + bool success; DEBUGASSERT(!data->conn); memset(&match, 0, sizeof(match)); @@ -1355,16 +1332,17 @@ (needle->scheme->protocol & PROTO_FAMILY_HTTP); #endif #endif + match.req_tls = data->set.use_ssl >= CURLUSESSL_CONTROL; /* Find a connection in the pool that matches what "data + needle" * requires. If a suitable candidate is found, it is attached to "data". */ - result = Curl_cpool_find(data, needle->destination, - url_match_conn, url_match_result, &match); + success = Curl_cpool_find(data, needle->destination, + url_match_conn, url_match_result, &match); /* wait_pipe is TRUE if we encounter a bundle that is undecided. There * is no matching connection then, yet. */ *waitpipe = (bool)match.wait_pipe; - return result; + return success; } /* @@ -1384,7 +1362,7 @@ conn->send_idx = 0; /* default for sending transfer data */ conn->connection_id = -1; /* no ID */ conn->attached_xfers = 0; - conn->remote_port = -1; /* unknown at this point */ + conn->remote_port = 0; /* unknown at this point */ /* Store creation time to help future close decision making */ conn->created = *Curl_pgrs_now(data); @@ -1420,7 +1398,7 @@ conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; #endif conn->ip_version = data->set.ipver; - conn->connect_only = (bool)data->set.connect_only; + conn->bits.connect_only = (bool)data->set.connect_only; conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ /* Store the local bind parameters that will be used for this connection */ @@ -1451,80 +1429,6 @@ return NULL; } -const struct Curl_scheme *Curl_get_scheme(const char *scheme) -{ - return Curl_getn_scheme(scheme, strlen(scheme)); -} - -/* Returns a struct scheme pointer if the name is a known scheme. Check the - ->run struct field for non-NULL to figure out if an implementation is - present. */ -const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len) -{ - /* table generated by schemetable.c: - 1. gcc schemetable.c && ./a.out - 2. check how small the table gets - 3. tweak the hash algorithm, then rerun from 1 - 4. when the table is good enough - 5. copy the table into this source code - 6. make sure this function uses the same hash function that worked for - schemetable.c - */ - static const struct Curl_scheme * const all_schemes[67] = { - &Curl_scheme_file, - &Curl_scheme_mqtts, NULL, - &Curl_scheme_gophers, NULL, - &Curl_scheme_rtmpe, - &Curl_scheme_smtp, - &Curl_scheme_sftp, - &Curl_scheme_smb, - &Curl_scheme_smtps, - &Curl_scheme_telnet, - &Curl_scheme_gopher, - &Curl_scheme_tftp, NULL, NULL, NULL, - &Curl_scheme_ftps, - &Curl_scheme_http, - &Curl_scheme_imap, - &Curl_scheme_rtmps, - &Curl_scheme_rtmpt, NULL, NULL, NULL, - &Curl_scheme_ldaps, - &Curl_scheme_wss, - &Curl_scheme_https, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - &Curl_scheme_rtsp, - &Curl_scheme_smbs, - &Curl_scheme_scp, NULL, NULL, NULL, - &Curl_scheme_pop3, NULL, NULL, - &Curl_scheme_rtmp, NULL, NULL, NULL, - &Curl_scheme_rtmpte, NULL, NULL, NULL, - &Curl_scheme_dict, NULL, NULL, NULL, - &Curl_scheme_mqtt, - &Curl_scheme_pop3s, - &Curl_scheme_imaps, NULL, - &Curl_scheme_ws, NULL, - &Curl_scheme_rtmpts, - &Curl_scheme_ldap, NULL, NULL, - &Curl_scheme_ftp, - }; - - if(len && (len <= 7)) { - const char *s = scheme; - size_t l = len; - const struct Curl_scheme *h; - unsigned int c = 978; - while(l) { - c <<= 5; - c += (unsigned int)Curl_raw_tolower(*s); - s++; - l--; - } - - h = all_schemes[c % 67]; - if(h && curl_strnequal(scheme, h->name, len) && !h->name[len]) - return h; - } - return NULL; -} - static CURLcode findprotocol(struct Curl_easy *data, struct connectdata *conn, const char *protostr) @@ -1627,6 +1531,7 @@ CURLU *uh; CURLUcode uc; char *hostname; + size_t hlen; bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); up_free(data); /* cleanup previous leftovers first */ @@ -1685,17 +1590,16 @@ failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN); return CURLE_URL_MALFORMAT; } + hostname = data->state.up.hostname; + hlen = hostname ? strlen(hostname) : 0; if(hostname && hostname[0] == '[') { /* This looks like an IPv6 address literal. See if there is an address scope. */ - size_t hlen; - conn->bits.ipv6_ip = TRUE; - /* cut off the brackets! */ + /* cut off the brackets after copying this! */ hostname++; - hlen = strlen(hostname); - hostname[hlen - 1] = 0; + hlen -= 2; zonefrom_url(uh, data, conn); } @@ -1704,6 +1608,7 @@ conn->host.rawalloc = curlx_strdup(hostname ? hostname : ""); if(!conn->host.rawalloc) return CURLE_OUT_OF_MEMORY; + conn->host.rawalloc[hlen] = 0; /* cut off for ipv6 case */ conn->host.name = conn->host.rawalloc; /************************************************************* @@ -1719,7 +1624,7 @@ /* This MUST use the IDN decoded name */ if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { char *url; - Curl_safefree(data->state.up.scheme); + curlx_safefree(data->state.up.scheme); uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0); if(uc) return Curl_uc_to_curlcode(uc); @@ -1880,7 +1785,7 @@ struct connectdata *conn) { const char *hostname; - int port; + uint16_t port; CURLcode result; DEBUGF(infof(data, "setup connection, bits.close=%d", conn->bits.close)); @@ -1900,19 +1805,21 @@ else #endif { - port = conn->remote_port; - if(conn->bits.conn_to_host) - hostname = conn->conn_to_host.name; - else - hostname = conn->host.name; + port = conn->bits.conn_to_port ? + conn->conn_to_port : conn->remote_port; + hostname = conn->bits.conn_to_host ? + conn->conn_to_host.name : conn->host.name; } #ifdef USE_IPV6 - conn->destination = curl_maprintf("%u/%d/%s", conn->scope_id, port, - hostname); -#else - conn->destination = curl_maprintf("%d/%s", port, hostname); + /* IPv6 addresses with a scope_id (0 is default == global) have a + * printable representation with a '%<scope_id>' suffix. */ + if(conn->scope_id) + conn->destination = curl_maprintf("[%s:%u]%%%u", hostname, port, + conn->scope_id); + else #endif + conn->destination = curl_maprintf("%s:%u", hostname, port); if(!conn->destination) return CURLE_OUT_OF_MEMORY; @@ -2082,15 +1989,13 @@ goto error; } -#ifdef USE_SSL - if(!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) -#endif - if(IS_HTTPS_PROXY(proxytype)) { - failf(data, "Unsupported proxy \'%s\', libcurl is built without the " - "HTTPS-proxy support.", proxy); - result = CURLE_NOT_BUILT_IN; - goto error; - } + if(IS_HTTPS_PROXY(proxytype) && + !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { + failf(data, "Unsupported proxy \'%s\', libcurl is built without the " + "HTTPS-proxy support.", proxy); + result = CURLE_NOT_BUILT_IN; + goto error; + } sockstype = proxytype == CURLPROXY_SOCKS5_HOSTNAME || @@ -2101,7 +2006,7 @@ proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy; proxyinfo->proxytype = (unsigned char)proxytype; - /* Is there a username and password given in this proxy url? */ + /* Is there a username and password given in this proxy URL? */ uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); if(uc && (uc != CURLUE_NO_USER)) { result = Curl_uc_to_curlcode(uc); @@ -2120,7 +2025,7 @@ proxyuser = NULL; if(result) goto error; - Curl_safefree(proxyinfo->passwd); + curlx_safefree(proxyinfo->passwd); if(!proxypasswd) { proxypasswd = curlx_strdup(""); if(!proxypasswd) { @@ -2239,7 +2144,7 @@ if(conn->http_proxy.passwd) result = CURLE_OK; else - Curl_safefree(conn->http_proxy.user); + curlx_safefree(conn->http_proxy.user); } return result; } @@ -2300,15 +2205,15 @@ if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? data->set.str[STRING_NOPROXY] : no_proxy)) { - Curl_safefree(proxy); - Curl_safefree(socksproxy); + curlx_safefree(proxy); + curlx_safefree(socksproxy); } #ifndef CURL_DISABLE_HTTP else if(!proxy && !socksproxy) /* if the host is not in the noproxy list, detect proxy. */ proxy = detect_proxy(data, conn); #endif /* CURL_DISABLE_HTTP */ - Curl_safefree(no_proxy); + curlx_safefree(no_proxy); #ifdef USE_UNIX_SOCKETS /* For the time being do not mix proxy and Unix domain sockets. See #1274 */ @@ -2337,18 +2242,18 @@ * connection that may exist registered to the same proxy host. ***********************************************************************/ if(proxy || socksproxy) { - long ptype = conn->http_proxy.proxytype; if(proxy) { - result = parse_proxy(data, conn, proxy, ptype); - Curl_safefree(proxy); /* parse_proxy copies the proxy string */ + result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype); + curlx_safefree(proxy); /* parse_proxy copies the proxy string */ if(result) goto out; } if(socksproxy) { - result = parse_proxy(data, conn, socksproxy, ptype); + result = parse_proxy(data, conn, socksproxy, + conn->socks_proxy.proxytype); /* parse_proxy copies the socks proxy string */ - Curl_safefree(socksproxy); + curlx_safefree(socksproxy); if(result) goto out; } @@ -2573,8 +2478,8 @@ #ifndef CURL_DISABLE_NETRC if(data->set.use_netrc == CURL_NETRC_REQUIRED) { - Curl_safefree(*userp); - Curl_safefree(*passwdp); + curlx_safefree(*userp); + curlx_safefree(*passwdp); } conn->bits.netrc = FALSE; if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) { @@ -2835,19 +2740,15 @@ ptr++; } else { - /* check whether the URL's hostname matches */ - size_t hostname_to_match_len; - char *hostname_to_match = curl_maprintf("%s%s%s", - conn->bits.ipv6_ip ? "[" : "", - conn->host.name, - conn->bits.ipv6_ip ? "]" : ""); - if(!hostname_to_match) - return CURLE_OUT_OF_MEMORY; - hostname_to_match_len = strlen(hostname_to_match); - host_match = curl_strnequal(ptr, hostname_to_match, - hostname_to_match_len); - curlx_free(hostname_to_match); - ptr += hostname_to_match_len; + /* check whether the URL's hostname matches. Use the URL hostname + * when it was an IPv6 address. Otherwise use the connection's hostname + * that has IDN conversion. */ + char *hostname_to_match = + (data->state.up.hostname && data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name; + size_t hlen = strlen(hostname_to_match); + host_match = curl_strnequal(ptr, hostname_to_match, hlen); + ptr += hlen; host_match = host_match && *ptr == ':'; ptr++; @@ -2908,13 +2809,13 @@ else { /* no "connect to host" */ conn->bits.conn_to_host = FALSE; - Curl_safefree(host); + curlx_safefree(host); } if(port >= 0) { - conn->conn_to_port = port; + conn->conn_to_port = (uint16_t)port; conn->bits.conn_to_port = TRUE; - infof(data, "Connecting to port: %d", port); + infof(data, "Connecting to port: %u", conn->conn_to_port); } else { /* no "connect to port" */ @@ -3016,7 +2917,7 @@ conn->conn_to_port = as->dst.port; conn->bits.conn_to_port = TRUE; conn->bits.altused = TRUE; - infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d", + infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u", Curl_alpnid2str(srcalpnid), host, conn->remote_port, Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); if(srcalpnid != as->dst.alpnid) { @@ -3044,119 +2945,9 @@ return result; } -#ifdef USE_UNIX_SOCKETS -static CURLcode resolve_unix(struct Curl_easy *data, - struct connectdata *conn, - const char *unix_path, - struct Curl_dns_entry **pdns) -{ - struct Curl_dns_entry *hostaddr; - bool longpath = FALSE; - - DEBUGASSERT(unix_path); - *pdns = NULL; - - /* Unix domain sockets are local. The host gets ignored, use the specified - * domain socket address. Do not cache "DNS entries". There is no DNS - * involved and we already have the file system path available. */ - hostaddr = curlx_calloc(1, sizeof(struct Curl_dns_entry)); - if(!hostaddr) - return CURLE_OUT_OF_MEMORY; - - hostaddr->addr = Curl_unix2addr(unix_path, &longpath, - (bool)conn->bits.abstract_unix_socket); - if(!hostaddr->addr) { - if(longpath) - /* Long paths are not supported for now */ - failf(data, "Unix socket path too long: '%s'", unix_path); - curlx_free(hostaddr); - return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY; - } - - hostaddr->refcount = 1; /* connection is the only one holding this */ - *pdns = hostaddr; - return CURLE_OK; -} -#endif - -/************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ -static CURLcode resolve_server(struct Curl_easy *data, - struct connectdata *conn, - bool *async, - struct Curl_dns_entry **pdns) -{ - struct hostname *ehost; - int eport; - timediff_t timeout_ms = Curl_timeleft_ms(data); - const char *peertype = "host"; - CURLcode result; - - *pdns = NULL; - -#ifdef USE_UNIX_SOCKETS - { - const char *unix_path = conn->unix_domain_socket; - -#ifndef CURL_DISABLE_PROXY - if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name && - !strncmp(UNIX_SOCKET_PREFIX "/", - conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX))) - unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1; -#endif - - if(unix_path) { - /* This only works if previous transport is TRNSPRT_TCP. Check it? */ - conn->transport_wanted = TRNSPRT_UNIX; - return resolve_unix(data, conn, unix_path, pdns); - } - } -#endif - -#ifndef CURL_DISABLE_PROXY - if(CONN_IS_PROXIED(conn)) { - ehost = conn->bits.socksproxy ? &conn->socks_proxy.host : - &conn->http_proxy.host; - eport = conn->bits.socksproxy ? conn->socks_proxy.port : - conn->http_proxy.port; - peertype = "proxy"; - } - else -#endif - { - ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host; - /* If not connecting via a proxy, extract the port from the URL, if it is - * there, thus overriding any defaults that might have been set above. */ - eport = conn->bits.conn_to_port ? conn->conn_to_port : conn->remote_port; - } - - result = Curl_resolv_timeout(data, ehost->name, - eport, conn->ip_version, - pdns, timeout_ms); - DEBUGASSERT(!result || !*pdns); - if(result == CURLE_AGAIN) { - *async = TRUE; - return CURLE_OK; - } - else if(result == CURLE_OPERATION_TIMEDOUT) { - failf(data, "Failed to resolve %s '%s' with timeout after %" - FMT_TIMEDIFF_T " ms", peertype, ehost->dispname, - curlx_ptimediff_ms(Curl_pgrs_now(data), - &data->progress.t_startsingle)); - return CURLE_OPERATION_TIMEDOUT; - } - else if(result) { - failf(data, "Could not resolve %s: %s", peertype, ehost->dispname); - return result; - } - DEBUGASSERT(*pdns); - return CURLE_OK; -} - static void url_move_hostname(struct hostname *dest, struct hostname *src) { - Curl_safefree(dest->rawalloc); + curlx_safefree(dest->rawalloc); Curl_free_idnconverted_hostname(dest); *dest = *src; memset(src, 0, sizeof(*src)); @@ -3222,7 +3013,7 @@ static void conn_meta_freeentry(void *p) { (void)p; - /* Will always be FALSE. Cannot use a 0 assert here since compilers + /* Always FALSE. Cannot use a 0 assert here since compilers * are not in agreement if they then want a NORETURN attribute or * not. *sigh* */ DEBUGASSERT(p == NULL); @@ -3399,6 +3190,10 @@ needle->recv[SECONDARYSOCKET] = Curl_cf_recv; needle->send[SECONDARYSOCKET] = Curl_cf_send; needle->bits.tcp_fastopen = data->set.tcp_fastopen; +#ifdef USE_UNIX_SOCKETS + if(Curl_conn_get_unix_path(needle)) + needle->transport_wanted = TRNSPRT_UNIX; +#endif } out: @@ -3621,41 +3416,12 @@ return result; } -/* Curl_setup_conn() is called after the name resolve initiated in - * create_conn() is all done. - * - * Curl_setup_conn() also handles reused connections - */ -CURLcode Curl_setup_conn(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_done) -{ - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - - DEBUGASSERT(dns); - Curl_pgrsTime(data, TIMER_NAMELOOKUP); - - if(!conn->bits.reuse) - result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns, - CURL_CF_SSL_DEFAULT); - if(!result) - result = Curl_headers_init(data); - - /* not sure we need this flag to be passed around any more */ - *protocol_done = FALSE; - return result; -} - -CURLcode Curl_connect(struct Curl_easy *data, - bool *asyncp, - bool *protocol_done) +CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) { CURLcode result; struct connectdata *conn; - *asyncp = FALSE; /* assume synchronous resolves by default */ - *protocol_done = FALSE; + *pconnected = FALSE; /* Set the request to virgin state based on transfer settings */ Curl_req_hard_reset(&data->req, data); @@ -3664,40 +3430,31 @@ result = url_find_or_create_conn(data); conn = data->conn; - if(result == CURLE_NO_CONNECTION_AVAILABLE) { - DEBUGASSERT(!conn); - return result; + if(result) + goto out; + + DEBUGASSERT(conn); + Curl_pgrsTime(data, TIMER_POSTQUEUE); + if(conn->bits.reuse) { + if(conn->attached_xfers > 1) + /* multiplexed */ + *pconnected = TRUE; + } + else if(conn->scheme->flags & PROTOPT_NONETWORK) { + Curl_pgrsTime(data, TIMER_NAMELOOKUP); + *pconnected = TRUE; + } + else { + result = Curl_conn_setup(data, conn, FIRSTSOCKET, NULL, + CURL_CF_SSL_DEFAULT); + if(!result) + result = Curl_headers_init(data); + CURL_TRC_M(data, "Curl_conn_setup() -> %d", result); } - if(!result) { - DEBUGASSERT(conn); - Curl_pgrsTime(data, TIMER_POSTQUEUE); - if(conn->bits.reuse) { - if(conn->attached_xfers > 1) - /* multiplexed */ - *protocol_done = TRUE; - } - else if(conn->scheme->flags & PROTOPT_NONETWORK) { - *asyncp = FALSE; - Curl_pgrsTime(data, TIMER_NAMELOOKUP); - *protocol_done = TRUE; - } - else { - /************************************************************* - * Resolve the address of the server or proxy - *************************************************************/ - struct Curl_dns_entry *dns; - result = resolve_server(data, conn, asyncp, &dns); - if(!result) { - *asyncp = !dns; - if(dns) - /* DNS resolution is done: that is either because this is a reused - connection, in which case DNS was unnecessary, or because DNS - really did finish already (synch resolver/fast async resolve) */ - result = Curl_setup_conn(data, dns, protocol_done); - } - } - } +out: + if(result == CURLE_NO_CONNECTION_AVAILABLE) + DEBUGASSERT(!conn); if(result && conn) { /* We are not allowed to return failure with memory left allocated in the @@ -3734,6 +3491,7 @@ data->state.done = FALSE; /* *_done() is not called yet */ + data->req.no_body = data->set.opt_no_body; if(data->req.no_body) /* in HTTP lingo, no body means using the HEAD request... */ data->state.httpreq = HTTPREQ_HEAD; @@ -3865,11 +3623,6 @@ return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1); } -CURLcode Curl_1st_err(CURLcode r1, CURLcode r2) -{ - return r1 ? r1 : r2; -} - CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2) { if(r1 && (r1 != CURLE_AGAIN))
diff --git a/Utilities/cmcurl/lib/url.h b/Utilities/cmcurl/lib/url.h index c4cd762..66baf70 100644 --- a/Utilities/cmcurl/lib/url.h +++ b/Utilities/cmcurl/lib/url.h
@@ -26,7 +26,7 @@ #include "curl_setup.h" /* - * Prototypes for library-wide functions provided by url.c + * Prototypes for library-wide functions */ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn); @@ -36,12 +36,7 @@ void Curl_freeset(struct Curl_easy *data); CURLcode Curl_uc_to_curlcode(CURLUcode uc); CURLcode Curl_close(struct Curl_easy **datap); /* opposite of Curl_open() */ -CURLcode Curl_connect(struct Curl_easy *data, - bool *asyncp, - bool *protocol_done); -CURLcode Curl_setup_conn(struct Curl_easy *data, - struct Curl_dns_entry *dns, - bool *protocol_done); +CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected); void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn); CURLcode Curl_parse_login_details(const char *login, const size_t len, char **userp, char **passwdp, @@ -69,13 +64,6 @@ void Curl_conn_meta_remove(struct connectdata *conn, const char *key); void *Curl_conn_meta_get(struct connectdata *conn, const char *key); -/* Get protocol handler for a URI scheme - * @param scheme URI scheme, case-insensitive - * @return NULL of handler not found - */ -const struct Curl_scheme *Curl_get_scheme(const char *scheme); -const struct Curl_scheme *Curl_getn_scheme(const char *scheme, size_t len); - #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */ #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless specified */ @@ -93,15 +81,8 @@ struct connectdata *conn); /** - * Always eval all arguments, return the first result != CURLE_OK. - * A non-short-circuit evaluation. - */ -CURLcode Curl_1st_err(CURLcode r1, CURLcode r2); - -/** * Always eval all arguments, return the first - * result != (CURLE_OK|CURLE_AGAIN) or `r1`. - * A non-short-circuit evaluation. + * result != (CURLE_OK | CURLE_AGAIN) or `r1`. */ CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2);
diff --git a/Utilities/cmcurl/lib/urlapi-int.h b/Utilities/cmcurl/lib/urlapi-int.h index 29d4fe5..4d8f2c1 100644 --- a/Utilities/cmcurl/lib/urlapi-int.h +++ b/Utilities/cmcurl/lib/urlapi-int.h
@@ -24,6 +24,34 @@ * ***************************************************************************/ #include "curl_setup.h" +#include <curl/urlapi.h> + +/* Internal representation of CURLU. Point to URL-encoded strings. */ +struct Curl_URL { + char *scheme; + char *user; + char *password; + char *options; /* IMAP only? */ + char *host; + char *zoneid; /* for numerical IPv6 addresses */ + char *port; + char *path; + char *query; + char *fragment; + unsigned short portnum; /* the numerical version (if 'port' is set) */ + BIT(query_present); /* to support blank */ + BIT(fragment_present); /* to support blank */ + BIT(guessed_scheme); /* when a URL without scheme is parsed */ +}; + +#define HOST_ERROR (-1) /* out of memory */ +#define HOST_NAME 1 +#define HOST_IPV4 2 +#define HOST_IPV6 3 + +#define QUERY_NO 2 +#define QUERY_NOT_YET 3 /* allow to change to query */ +#define QUERY_YES 4 size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, bool guess_scheme); @@ -32,12 +60,9 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace); -#ifdef UNITTESTS -UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, - bool has_scheme); -#endif - -#define U_CURLU_URLDECODE (unsigned int)CURLU_URLDECODE +#define U_CURLU_URLDECODE (unsigned int)CURLU_URLDECODE #define U_CURLU_PATH_AS_IS (unsigned int)CURLU_PATH_AS_IS +bool Curl_url_same_origin(CURLU *base, CURLU *href); + #endif /* HEADER_CURL_URLAPI_INT_H */
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c index a4b82f3..103e0a6 100644 --- a/Utilities/cmcurl/lib/urlapi.c +++ b/Utilities/cmcurl/lib/urlapi.c
@@ -63,24 +63,6 @@ #define AF_INET6 (AF_INET + 1) #endif -/* Internal representation of CURLU. Point to URL-encoded strings. */ -struct Curl_URL { - char *scheme; - char *user; - char *password; - char *options; /* IMAP only? */ - char *host; - char *zoneid; /* for numerical IPv6 addresses */ - char *port; - char *path; - char *query; - char *fragment; - unsigned short portnum; /* the numerical version (if 'port' is set) */ - BIT(query_present); /* to support blank */ - BIT(fragment_present); /* to support blank */ - BIT(guessed_scheme); /* when a URL without scheme is parsed */ -}; - #define DEFAULT_SCHEME "https" static void free_urlhandle(struct Curl_URL *u) @@ -124,19 +106,34 @@ /* urlencode_str() writes data into an output dynbuf and URL-encodes the * spaces in the source URL accordingly. * + * This function re-encodes the string, meaning that it leaves already encoded + * bytes as-is and works by encoding only what *has* to be encoded - unless it + * has to uppercase the hex to normalize. + * + * Illegal percent-encoding sequences are left as-is. + * * URL encoding should be skipped for hostnames, otherwise IDN resolution * will fail. + * + * 'query' tells if it is a query part or not, or if it is allowed to + * "transition" into a query part with a question mark. + * + * @unittest 1675 */ -static CURLUcode urlencode_str(struct dynbuf *o, const char *url, - size_t len, bool relative, - bool query) +UNITTEST CURLUcode urlencode_str(struct dynbuf *o, const char *url, + size_t len, bool relative, + unsigned int query); +UNITTEST CURLUcode urlencode_str(struct dynbuf *o, const char *url, + size_t len, bool relative, + unsigned int query) { /* we must add this with whitespace-replacing */ - bool left = !query; const unsigned char *iptr; const unsigned char *host_sep = (const unsigned char *)url; CURLcode result = CURLE_OK; + DEBUGASSERT((query >= QUERY_NO) && (query <= QUERY_YES)); + if(!relative) { size_t n; host_sep = (const unsigned char *)find_host_sep(url); @@ -149,7 +146,7 @@ for(iptr = host_sep; len && !result; iptr++, len--) { if(*iptr == ' ') { - if(left) + if(query != QUERY_YES) result = curlx_dyn_addn(o, "%20", 3); else result = curlx_dyn_addn(o, "+", 1); @@ -159,10 +156,22 @@ Curl_hexbyte(&out[1], *iptr); result = curlx_dyn_addn(o, out, 3); } + else if(*iptr == '%' && (len >= 3) && + ISXDIGIT(iptr[1]) && ISXDIGIT(iptr[2]) && + (ISLOWER(iptr[1]) || ISLOWER(iptr[2]))) { + /* uppercase it */ + unsigned char hex = (unsigned char)((curlx_hexval(iptr[1]) << 4) | + curlx_hexval(iptr[2])); + unsigned char out[3] = { '%' }; + Curl_hexbyte(&out[1], hex); + result = curlx_dyn_addn(o, out, 3); + iptr += 2; + len -= 2; + } else { result = curlx_dyn_addn(o, iptr, 1); - if(*iptr == '?') - left = FALSE; + if(*iptr == '?' && (query == QUERY_NOT_YET)) + query = QUERY_YES; } } @@ -251,8 +260,8 @@ unsigned int flags, size_t *offset) /* to the hostname */ { - CURLUcode result = CURLUE_OK; - CURLcode ccode; + CURLUcode ures = CURLUE_OK; + CURLcode result; char *userp = NULL; char *passwdp = NULL; char *optionsp = NULL; @@ -285,21 +294,21 @@ /* We could use the login information in the URL so extract it. Only parse options if the handler says we should. Note that 'h' might be NULL! */ - ccode = Curl_parse_login_details(login, ptr - login - 1, - &userp, &passwdp, - (h && (h->flags & PROTOPT_URLOPTIONS)) ? - &optionsp : NULL); - if(ccode) { + result = Curl_parse_login_details(login, ptr - login - 1, + &userp, &passwdp, + (h && (h->flags & PROTOPT_URLOPTIONS)) ? + &optionsp : NULL); + if(result) { /* the only possible error from Curl_parse_login_details is out of memory: */ - result = CURLUE_OUT_OF_MEMORY; + ures = CURLUE_OUT_OF_MEMORY; goto out; } if(userp) { if(flags & CURLU_DISALLOW_USER) { /* Option DISALLOW_USER is set and URL contains username. */ - result = CURLUE_USER_NOT_ALLOWED; + ures = CURLUE_USER_NOT_ALLOWED; goto out; } curlx_free(u->user); @@ -329,11 +338,14 @@ u->password = NULL; u->options = NULL; - return result; + return ures; } -UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host, - bool has_scheme) +/* @unittest 1653 */ +UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme); +UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, + bool has_scheme) { const char *portptr; const char *hostname = curlx_dyn_ptr(host); @@ -386,9 +398,15 @@ return CURLUE_OK; } -/* this assumes 'hostname' now starts with [ */ -static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, - size_t hlen) /* length of hostname */ +/* This function assumes 'hostname' now starts with [. It trims 'hostname' in + * place and it sets u->zoneid if present. + * + * @unittest 1675 + */ +UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, + size_t hlen); +UNITTEST CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, + size_t hlen) /* length of hostname */ { size_t len; DEBUGASSERT(*hostname == '['); @@ -472,15 +490,12 @@ * integers. * * Returns the host type. + * + * @unittest 1675 */ -#define HOST_ERROR (-1) /* out of memory */ - -#define HOST_NAME 1 -#define HOST_IPV4 2 -#define HOST_IPV6 3 - -static int ipv4_normalize(struct dynbuf *host) +UNITTEST int ipv4_normalize(struct dynbuf *host); +UNITTEST int ipv4_normalize(struct dynbuf *host) { bool done = FALSE; int n = 0; @@ -624,7 +639,7 @@ goto out; } - uc = Curl_parse_port(u, host, has_scheme); + uc = parse_port(u, host, has_scheme); if(uc) goto out; @@ -657,21 +672,21 @@ /* used for HTTP/2 server push */ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority) { - CURLUcode result; + CURLUcode ures; struct dynbuf host; DEBUGASSERT(authority); curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - result = parse_authority(u, authority, strlen(authority), - CURLU_DISALLOW_USER, &host, !!u->scheme); - if(result) + ures = parse_authority(u, authority, strlen(authority), + CURLU_DISALLOW_USER, &host, !!u->scheme); + if(ures) curlx_dyn_free(&host); else { curlx_free(u->host); u->host = curlx_dyn_ptr(&host); } - return result; + return ures; } /* @@ -700,7 +715,6 @@ /* * dedotdotify() - * @unittest: 1395 * * This function gets a null-terminated path with dot and dotdot sequences * passed in and strips them off according to the rules in RFC 3986 section @@ -711,6 +725,8 @@ * RETURNS * * Zero for success and 'out' set to an allocated dedotdotified string. + * + * @unittest 1395 */ UNITTEST int dedotdotify(const char *input, size_t clen, char **outp); UNITTEST int dedotdotify(const char *input, size_t clen, char **outp) @@ -718,38 +734,36 @@ struct dynbuf out; CURLcode result = CURLE_OK; + /* variables for leading dot checks */ + const char *dinput = input; + size_t dlen = clen; + *outp = NULL; - /* the path always starts with a slash, and a slash has not dot */ + /* a single byte path cannot be cleaned up */ if(clen < 2) return 0; curlx_dyn_init(&out, clen + 1); - /* A. If the input buffer begins with a prefix of "../" or "./", then - remove that prefix from the input buffer; otherwise, */ - if(is_dot(&input, &clen)) { - const char *p = input; - size_t blen = clen; - - if(!clen) - /* . [end] */ - goto end; - else if(ISSLASH(*p)) { + /* if the input buffer begins with a prefix of "../" or "./", then remove + that prefix from the input buffer; otherwise, */ + if(is_dot(&dinput, &dlen)) { + if(ISSLASH(*dinput)) { /* one dot followed by a slash */ - input = p + 1; - clen--; + input = dinput + 1; + clen = dlen - 1; } - /* D. if the input buffer consists only of "." or "..", then remove - that from the input buffer; otherwise, */ - else if(is_dot(&p, &blen)) { - if(!blen) + /* if the input buffer consists only of "." or "..", then remove + that from the input buffer; otherwise, */ + else if(is_dot(&dinput, &dlen)) { + if(!dlen) /* .. [end] */ goto end; - else if(ISSLASH(*p)) { + else if(ISSLASH(*dinput)) { /* ../ */ - input = p + 1; - clen = blen - 1; + input = dinput + 1; + clen = dlen - 1; } } } @@ -758,9 +772,9 @@ if(ISSLASH(*input)) { const char *p = &input[1]; size_t blen = clen - 1; - /* B. if the input buffer begins with a prefix of "/./" or "/.", where - "." is a complete path segment, then replace that prefix with "/" in - the input buffer; otherwise, */ + /* if the input buffer begins with a prefix of "/./" or "/.", where "." + is a complete path segment, then replace that prefix with "/" in the + input buffer; otherwise, */ if(is_dot(&p, &blen)) { if(!blen) { /* /. */ result = curlx_dyn_addn(&out, "/", 1); @@ -772,10 +786,10 @@ continue; } - /* C. if the input buffer begins with a prefix of "/../" or "/..", - where ".." is a complete path segment, then replace that prefix - with "/" in the input buffer and remove the last segment and its - preceding "/" (if any) from the output buffer; otherwise, */ + /* if the input buffer begins with a prefix of "/../" or "/..", where + ".." is a complete path segment, then replace that prefix with "/" + in the input buffer and remove the last segment and its preceding + "/" (if any) from the output buffer; otherwise, */ else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) { /* remove the last segment from the output buffer */ size_t len = curlx_dyn_len(&out); @@ -798,10 +812,10 @@ } } - /* E. move the first path segment in the input buffer to the end of - the output buffer, including the initial "/" character (if any) and - any subsequent characters up to, but not including, the next "/" - character or the end of the input buffer. */ + /* move the first path segment in the input buffer to the end of the + output buffer, including the initial "/" character (if any) and any + subsequent characters up to, but not including, the next "/" character + or the end of the input buffer. */ result = curlx_dyn_addn(&out, input, 1); input++; @@ -820,13 +834,19 @@ return result ? 1 : 0; /* success */ } -static CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, - struct dynbuf *host, const char **pathp, - size_t *pathlenp) +/* + * @unittest 1675 + */ +UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, + const char **pathp, size_t *pathlenp); +UNITTEST CURLUcode parse_file(const char *url, size_t urllen, CURLU *u, + const char **pathp, size_t *pathlenp) { const char *path; size_t pathlen; - bool uncpath = FALSE; + + *pathp = NULL; + *pathlenp = 0; if(urllen <= 6) /* file:/ is not enough to actually be a complete file: URL */ return CURLUE_BAD_FILE_URL; @@ -835,10 +855,6 @@ path = &url[5]; pathlen = urllen - 5; - u->scheme = curlx_strdup("file"); - if(!u->scheme) - return CURLUE_OUT_OF_MEMORY; - /* Extra handling URLs with an authority component (i.e. that start with * "file://") * @@ -858,9 +874,6 @@ * * o the hostname is a FQDN that resolves to this machine, or * - * o it is an UNC String transformed to an URI (Windows only, RFC 8089 - * Appendix E.3). - * * For brevity, we only consider URLs with empty, "localhost", or * "127.0.0.1" hostnames as local, otherwise as an UNC String. * @@ -875,42 +888,16 @@ checkprefix("127.0.0.1/", ptr)) { ptr += 9; /* now points to the slash after the host */ } - else { -#ifdef _WIN32 - size_t len; - - /* the hostname, NetBIOS computer name, can not contain disallowed - chars, and the delimiting slash character must be appended to the - hostname */ - path = strpbrk(ptr, "/\\:*?\"<>|"); - if(!path || *path != '/') - return CURLUE_BAD_FILE_URL; - - len = path - ptr; - if(len) { - CURLcode code = curlx_dyn_addn(host, ptr, len); - if(code) - return cc2cu(code); - uncpath = TRUE; - } - - ptr -= 2; /* now points to the // before the host in UNC */ -#else + else /* Invalid file://hostname/, expected localhost or 127.0.0.1 or none */ return CURLUE_BAD_FILE_URL; -#endif - } } path = ptr; pathlen = urllen - (ptr - url); } - if(!uncpath) - /* no host for file: URLs by default */ - curlx_dyn_reset(host); - #if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__) /* Do not allow Windows drive letters when not in Windows. * This catches both "file:/c:" and "file:c:" */ @@ -927,6 +914,10 @@ pathlen--; } #endif + u->scheme = curlx_strdup("file"); + if(!u->scheme) + return CURLUE_OUT_OF_MEMORY; + *pathp = path; *pathlenp = pathlen; return CURLUE_OK; @@ -1012,16 +1003,16 @@ static CURLUcode handle_fragment(CURLU *u, const char *fragment, size_t fraglen, unsigned int flags) { - CURLUcode result; + CURLUcode ures; u->fragment_present = TRUE; if(fraglen > 1) { /* skip the leading '#' in the copy but include the terminating null */ if(flags & CURLU_URLENCODE) { struct dynbuf enc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE); - if(result) - return result; + ures = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, QUERY_NO); + if(ures) + return ures; u->fragment = curlx_dyn_ptr(&enc); } else { @@ -1040,12 +1031,12 @@ if(qlen > 1) { if(flags & CURLU_URLENCODE) { struct dynbuf enc; - CURLUcode result; + CURLUcode ures; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); /* skip the leading question mark */ - result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE); - if(result) - return result; + ures = urlencode_str(&enc, query + 1, qlen - 1, TRUE, QUERY_YES); + if(ures) + return ures; u->query = curlx_dyn_ptr(&enc); } else { @@ -1064,24 +1055,22 @@ } static CURLUcode handle_path(CURLU *u, const char *path, - size_t pathlen, unsigned int flags) + size_t pathlen, unsigned int flags, + bool is_file) { - CURLUcode result; + CURLUcode ures; if(pathlen && (flags & CURLU_URLENCODE)) { struct dynbuf enc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - result = urlencode_str(&enc, path, pathlen, TRUE, FALSE); - if(result) - return result; + ures = urlencode_str(&enc, path, pathlen, TRUE, QUERY_NO); + if(ures) + return ures; pathlen = curlx_dyn_len(&enc); path = u->path = curlx_dyn_ptr(&enc); } - if(pathlen <= 1) { - /* there is no path left or the slash, unset */ - path = NULL; - } - else { + if(pathlen >= (size_t)(1 + !is_file)) { + /* paths for file:// scheme can be one byte, others need to be two */ if(!u->path) { u->path = curlx_memdup0(path, pathlen); if(!u->path) @@ -1114,15 +1103,16 @@ char schemebuf[MAX_SCHEME_LEN + 1]; size_t schemelen = 0; size_t urllen; - CURLUcode result = CURLUE_OK; + CURLUcode ures = CURLUE_OK; struct dynbuf host; + bool is_file = FALSE; DEBUGASSERT(url); curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH); - result = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE)); - if(result) + ures = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE)); + if(ures) goto fail; schemelen = Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf), @@ -1130,13 +1120,15 @@ CURLU_DEFAULT_SCHEME)); /* handle the file: scheme */ - if(schemelen && !strcmp(schemebuf, "file")) - result = parse_file(url, urllen, u, &host, &path, &pathlen); + if(schemelen && !strcmp(schemebuf, "file")) { + is_file = TRUE; + ures = parse_file(url, urllen, u, &path, &pathlen); + } else { const char *hostp = NULL; size_t hostlen; - result = parse_scheme(url, u, schemebuf, schemelen, flags, &hostp); - if(result) + ures = parse_scheme(url, u, schemebuf, schemelen, flags, &hostp); + if(ures) goto fail; /* find the end of the hostname + port number */ @@ -1146,49 +1138,49 @@ /* this pathlen also contains the query and the fragment */ pathlen = urllen - (path - url); if(hostlen) { - result = parse_authority(u, hostp, hostlen, flags, &host, - u->scheme != NULL); - if(!result && (flags & CURLU_GUESS_SCHEME) && !u->scheme) - result = guess_scheme(u, &host); + ures = parse_authority(u, hostp, hostlen, flags, &host, + u->scheme != NULL); + if(!ures && (flags & CURLU_GUESS_SCHEME) && !u->scheme) + ures = guess_scheme(u, &host); } else if(flags & CURLU_NO_AUTHORITY) { /* allowed to be empty. */ if(curlx_dyn_add(&host, "")) - result = CURLUE_OUT_OF_MEMORY; + ures = CURLUE_OUT_OF_MEMORY; } else - result = CURLUE_NO_HOST; + ures = CURLUE_NO_HOST; } - if(!result) { + if(!ures) { /* The path might at this point contain a fragment and/or a query to handle */ const char *fragment = strchr(path, '#'); if(fragment) { size_t fraglen = pathlen - (fragment - path); - result = handle_fragment(u, fragment, fraglen, flags); + ures = handle_fragment(u, fragment, fraglen, flags); /* after this, pathlen still contains the query */ pathlen -= fraglen; } } - if(!result) { + if(!ures) { const char *query = memchr(path, '?', pathlen); if(query) { size_t qlen = pathlen - (query - path); - result = handle_query(u, query, qlen, flags); + ures = handle_query(u, query, qlen, flags); pathlen -= qlen; } } - if(!result) + if(!ures) /* the fragment and query parts are trimmed off from the path */ - result = handle_path(u, path, pathlen, flags); - if(!result) { + ures = handle_path(u, path, pathlen, flags, is_file); + if(!ures) { u->host = curlx_dyn_ptr(&host); return CURLUE_OK; } fail: curlx_dyn_free(&host); free_urlhandle(u); - return result; + return ures; } /* @@ -1197,15 +1189,15 @@ static CURLUcode parseurl_and_replace(const char *url, CURLU *u, unsigned int flags) { - CURLUcode result; + CURLUcode ures; CURLU tmpurl; memset(&tmpurl, 0, sizeof(tmpurl)); - result = parseurl(url, &tmpurl, flags); - if(!result) { + ures = parseurl(url, &tmpurl, flags); + if(!ures) { free_urlhandle(u); *u = tmpurl; } - return result; + return ures; } /* @@ -1272,7 +1264,8 @@ curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH); if(!curlx_dyn_addn(&urlbuf, base, prelen) && - !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) { + !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, + QUERY_NOT_YET)) { uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u, flags & ~U_CURLU_PATH_AS_IS); } @@ -1380,11 +1373,12 @@ if(urldecode) { char *decoded; size_t dlen; - /* this unconditional rejection of control bytes is documented - API behavior */ - CURLcode res = Curl_urldecode(part, partlen, &decoded, &dlen, REJECT_CTRL); + /* this unconditional rejection of control bytes is documented API + behavior */ + CURLcode result = Curl_urldecode(part, partlen, &decoded, &dlen, + REJECT_CTRL); curlx_free(part); - if(res) + if(result) return CURLUE_URLDECODE; part = decoded; partlen = dlen; @@ -1392,7 +1386,8 @@ if(urlencode) { struct dynbuf enc; curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - uc = urlencode_str(&enc, part, partlen, TRUE, what == CURLUPART_QUERY); + uc = urlencode_str(&enc, part, partlen, TRUE, what == CURLUPART_QUERY ? + QUERY_YES : QUERY_NO); curlx_free(part); if(uc) return uc; @@ -1425,29 +1420,25 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) { char *url; - const char *scheme; - char *options = u->options; - char *port = u->port; char *allochost = NULL; - bool show_fragment = - u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY); - bool show_query = (u->query && u->query[0]) || - (u->query_present && flags & CURLU_GET_EMPTY); - bool punycode = (flags & CURLU_PUNYCODE) ? 1 : 0; - bool depunyfy = (flags & CURLU_PUNY2IDN) ? 1 : 0; - bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; + const char *fragmentsep = + (u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY)) ? + "#" : ""; + const char *querysep = ((u->query && u->query[0]) || + (u->query_present && flags & CURLU_GET_EMPTY)) ? + "?" : ""; char portbuf[7]; if(u->scheme && curl_strequal("file", u->scheme)) { url = curl_maprintf("file://%s%s%s%s%s", - u->path, - show_query ? "?" : "", - u->query ? u->query : "", - show_fragment ? "#" : "", - u->fragment ? u->fragment : ""); + u->path, querysep, u->query ? u->query : "", + fragmentsep, u->fragment ? u->fragment : ""); } else if(!u->host) return CURLUE_NO_HOST; else { + const char *scheme; + char *options = u->options; + char *port = u->port; const struct Curl_scheme *h = NULL; char schemebuf[MAX_SCHEME_LEN + 5]; if(u->scheme) @@ -1489,19 +1480,19 @@ allochost = curlx_dyn_ptr(&enc); } } - else if(urlencode) { + else if(flags & CURLU_URLENCODE) { allochost = curl_easy_escape(NULL, u->host, 0); if(!allochost) return CURLUE_OUT_OF_MEMORY; } - else if(punycode) { + else if(flags & CURLU_PUNYCODE) { if(!Curl_is_ASCII_name(u->host)) { CURLUcode ret = host_decode(u->host, &allochost); if(ret) return ret; } } - else if(depunyfy) { + else if(flags & CURLU_PUNY2IDN) { if(Curl_is_ASCII_name(u->host)) { CURLUcode ret = host_encode(u->host, &allochost); if(ret) @@ -1526,9 +1517,9 @@ port ? ":" : "", port ? port : "", u->path ? u->path : "/", - show_query ? "?" : "", + querysep, u->query ? u->query : "", - show_fragment ? "#" : "", + fragmentsep, u->fragment ? u->fragment : ""); curlx_free(allochost); } @@ -1649,6 +1640,7 @@ const char *s = scheme; if(ISALPHA(*s)) { /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ + s++; while(--plen) { if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.')) s++; /* fine */ @@ -1737,37 +1729,37 @@ memset(u, 0, sizeof(struct Curl_URL)); break; case CURLUPART_SCHEME: - Curl_safefree(u->scheme); + curlx_safefree(u->scheme); u->guessed_scheme = FALSE; break; case CURLUPART_USER: - Curl_safefree(u->user); + curlx_safefree(u->user); break; case CURLUPART_PASSWORD: - Curl_safefree(u->password); + curlx_safefree(u->password); break; case CURLUPART_OPTIONS: - Curl_safefree(u->options); + curlx_safefree(u->options); break; case CURLUPART_HOST: - Curl_safefree(u->host); + curlx_safefree(u->host); break; case CURLUPART_ZONEID: - Curl_safefree(u->zoneid); + curlx_safefree(u->zoneid); break; case CURLUPART_PORT: u->portnum = 0; - Curl_safefree(u->port); + curlx_safefree(u->port); break; case CURLUPART_PATH: - Curl_safefree(u->path); + curlx_safefree(u->path); break; case CURLUPART_QUERY: - Curl_safefree(u->query); + curlx_safefree(u->query); u->query_present = FALSE; break; case CURLUPART_FRAGMENT: - Curl_safefree(u->fragment); + curlx_safefree(u->fragment); u->fragment_present = FALSE; break; default: @@ -1845,7 +1837,7 @@ break; case CURLUPART_HOST: storep = &u->host; - Curl_safefree(u->zoneid); + curlx_safefree(u->zoneid); break; case CURLUPART_ZONEID: storep = &u->zoneid; @@ -1996,3 +1988,36 @@ } return CURLUE_OK; } + +bool Curl_url_same_origin(CURLU *base, CURLU *href) +{ + const struct Curl_scheme *s = NULL; + + /* base must be an absolute URL */ + if(!base->scheme || !base->host) + return FALSE; + if(href->scheme && !curl_strequal(base->scheme, href->scheme)) + return FALSE; + if(href->host) { + if(!curl_strequal(base->host, href->host)) + return FALSE; + if(!curl_strequal(base->port, href->port)) { + /* This may still match if only one has an explicit port + * and it is the default for the scheme. */ + if(base->port && href->port) + return FALSE; + + s = Curl_get_scheme(base->scheme); + if(!s) /* Cannot match default port for unknown scheme */ + return FALSE; + + /* The port which is set must be the default one */ + if((base->port && (base->portnum != s->defport)) || + (href->port && (href->portnum != s->defport))) + return FALSE; + } + } + else if(href->port) /* no host in href, then there must be no port */ + return FALSE; + return TRUE; +}
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h index 5ae1480..f35bfee 100644 --- a/Utilities/cmcurl/lib/urldata.h +++ b/Utilities/cmcurl/lib/urldata.h
@@ -26,70 +26,7 @@ /* This file is for lib internal stuff */ #include "curl_setup.h" -#define PORT_FTP 21 -#define PORT_FTPS 990 -#define PORT_TELNET 23 -#define PORT_HTTP 80 -#define PORT_HTTPS 443 -#define PORT_DICT 2628 -#define PORT_LDAP 389 -#define PORT_LDAPS 636 -#define PORT_TFTP 69 -#define PORT_SSH 22 -#define PORT_IMAP 143 -#define PORT_IMAPS 993 -#define PORT_POP3 110 -#define PORT_POP3S 995 -#define PORT_SMB 445 -#define PORT_SMBS 445 -#define PORT_SMTP 25 -#define PORT_SMTPS 465 /* sometimes called SSMTP */ -#define PORT_RTSP 554 -#define PORT_RTMP 1935 -#define PORT_RTMPT PORT_HTTP -#define PORT_RTMPS PORT_HTTPS -#define PORT_GOPHER 70 -#define PORT_MQTT 1883 -#define PORT_MQTTS 8883 - -#ifdef USE_ECH -/* CURLECH_ bits for the tls_ech option */ -#define CURLECH_DISABLE (1 << 0) -#define CURLECH_GREASE (1 << 1) -#define CURLECH_ENABLE (1 << 2) -#define CURLECH_HARD (1 << 3) -#define CURLECH_CLA_CFG (1 << 4) -#endif - -#ifndef CURL_DISABLE_WEBSOCKETS -/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number, - * the rest are internal information. If we use higher bits we only do this on - * platforms that have a >= 64-bit type and then we use such a type for the - * protocol fields in the protocol handler. - */ -#define CURLPROTO_WS (1L << 30) -#define CURLPROTO_WSS ((curl_prot_t)1 << 31) -#else -#define CURLPROTO_WS 0L -#define CURLPROTO_WSS 0L -#endif - -#define CURLPROTO_MQTTS (1LL << 32) - -#define CURLPROTO_64ALL ((uint64_t)0xffffffffffffffff) - -/* the default protocols accepting a redirect to */ -#define CURLPROTO_REDIR (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | \ - CURLPROTO_FTPS) - -typedef curl_off_t curl_prot_t; - -/* This mask is for all the old protocols that are provided and defined in the - public header and shall exclude protocols added since which are not exposed - in the API */ -#define CURLPROTO_MASK 0x3ffffff - -#define CURL_DEFAULT_USER "anonymous" +#define CURL_DEFAULT_USER "anonymous" #define CURL_DEFAULT_PASSWORD "ftp@example.com" #if !defined(_WIN32) && !defined(MSDOS) @@ -98,31 +35,11 @@ #define CURL_PREFER_LF_LINEENDS #endif -/* Convenience defines for checking protocols or their SSL based version. Each - protocol handler should only ever have a single CURLPROTO_ in its protocol - field. */ -#define PROTO_FAMILY_HTTP (CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_WS | \ - CURLPROTO_WSS) -#define PROTO_FAMILY_FTP (CURLPROTO_FTP | CURLPROTO_FTPS) -#define PROTO_FAMILY_POP3 (CURLPROTO_POP3 | CURLPROTO_POP3S) -#define PROTO_FAMILY_SMB (CURLPROTO_SMB | CURLPROTO_SMBS) -#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP | CURLPROTO_SMTPS) -#define PROTO_FAMILY_SSH (CURLPROTO_SCP | CURLPROTO_SFTP) - -#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) || \ - !defined(CURL_DISABLE_POP3) -/* these protocols support CURLOPT_DIRLISTONLY */ -#define CURL_LIST_ONLY_PROTOCOL 1 -#endif - #define DEFAULT_CONNCACHE_SIZE 5 /* length of longest IPv6 address string including the trailing null */ #define MAX_IPADR_LEN sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") -/* Default FTP/IMAP etc response timeout in milliseconds */ -#define RESP_TIMEOUT (60 * 1000) - /* Max string input length is a precaution against abuse and to detect junk input easier and better. */ #define CURL_MAX_INPUT_LENGTH 8000000 @@ -167,6 +84,7 @@ size_t *pnread); /* how much received */ #include "mime.h" +#include "protocol.h" #include "ftp.h" #include "http.h" #include "smb.h" @@ -322,16 +240,11 @@ GSS_AUTHSUCC } curlnegotiate; -#ifdef CURL_DISABLE_PROXY -#define CONN_IS_PROXIED(x) 0 -#else -#define CONN_IS_PROXIED(x) (x)->bits.proxy -#endif - /* * Boolean values that concerns this connection. */ struct ConnectBits { + BIT(connect_only); #ifndef CURL_DISABLE_PROXY BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ @@ -350,8 +263,6 @@ that overrides the host in the URL */ BIT(conn_to_port); /* if set, this connection has a "connect to port" that overrides the port in the URL (remote port) */ - BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6 - IP address */ BIT(ipv6); /* we communicate with a site using an IPv6 address */ BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is supposed to be called, after ->curl_do() */ @@ -378,9 +289,6 @@ BIT(multiplex); /* connection is multiplexed */ BIT(tcp_fastopen); /* use TCP Fast Open */ BIT(tls_enable_alpn); /* TLS ALPN extension? */ -#ifndef CURL_DISABLE_DOH - BIT(doh); -#endif #ifdef USE_UNIX_SOCKETS BIT(abstract_unix_socket); #endif @@ -393,6 +301,7 @@ BIT(shutdown_handler); /* connection shutdown: handler shut down */ BIT(shutdown_filters); /* connection shutdown: filters shut down */ BIT(in_cpool); /* connection is kept in a connection pool */ + BIT(dns_resolved); /* DNS records for connection were resolved */ }; struct hostname { @@ -402,165 +311,9 @@ const char *dispname; /* name to display, as 'name' might be encoded */ }; -/* - * Flags on the keepon member of the Curl_transfer_keeper - */ - -#define KEEP_NONE 0 -#define KEEP_RECV (1 << 0) /* there is or may be data to read */ -#define KEEP_SEND (1 << 1) /* there is or may be data to write */ - -/* transfer wants to send */ -#define CURL_WANT_SEND(data) ((data)->req.keepon & KEEP_SEND) -/* transfer wants to receive */ -#define CURL_WANT_RECV(data) ((data)->req.keepon & KEEP_RECV) - #define FIRSTSOCKET 0 #define SECONDARYSOCKET 1 -/* - * Specific protocol handler. - */ - -struct Curl_protocol { - /* Complement to setup_connection_internals(). This is done before the - transfer "owns" the connection. */ - CURLcode (*setup_connection)(struct Curl_easy *data, - struct connectdata *conn); - - /* These two functions MUST be set to be protocol dependent */ - CURLcode (*do_it)(struct Curl_easy *data, bool *done); - CURLcode (*done)(struct Curl_easy *, CURLcode, bool); - - /* If the curl_do() function is better made in two halves, this - * curl_do_more() function will be called afterwards, if set. For example - * for doing the FTP stuff after the PASV/PORT command. - */ - CURLcode (*do_more)(struct Curl_easy *, int *); - - /* This function *MAY* be set to a protocol-dependent function that is run - * after the connect() and everything is done, as a step in the connection. - * The 'done' pointer points to a bool that should be set to TRUE if the - * function completes before return. If it does not complete, the caller - * should call the ->connecting() function until it is. - */ - CURLcode (*connect_it)(struct Curl_easy *data, bool *done); - - /* See above. */ - CURLcode (*connecting)(struct Curl_easy *data, bool *done); - CURLcode (*doing)(struct Curl_easy *data, bool *done); - - /* Called from the multi interface during the PROTOCONNECT phase, and it - should then return a proper fd set */ - CURLcode (*proto_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DOING phase, and it should - then return a proper fd set */ - CURLcode (*doing_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DO_MORE phase, and it should - then return a proper fd set */ - CURLcode (*domore_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* Called from the multi interface during the DO_DONE, PERFORM and - WAITPERFORM phases, and it should then return a proper fd set. Not setting - this will make libcurl use the generic default one. */ - CURLcode (*perform_pollset)(struct Curl_easy *data, - struct easy_pollset *ps); - - /* This function *MAY* be set to a protocol-dependent function that is run - * by the curl_disconnect(), as a step in the disconnection. If the handler - * is called because the connection has been considered dead, - * dead_connection is set to TRUE. The connection is (again) associated with - * the transfer here. - */ - CURLcode (*disconnect)(struct Curl_easy *, struct connectdata *, - bool dead_connection); - - /* If used, this function gets called from transfer.c to - allow the protocol to do extra handling in writing response to - the client. */ - CURLcode (*write_resp)(struct Curl_easy *data, const char *buf, size_t blen, - bool is_eos); - - /* If used, this function gets called from transfer.c to - allow the protocol to do extra handling in writing a single response - header line to the client. */ - CURLcode (*write_resp_hd)(struct Curl_easy *data, - const char *hd, size_t hdlen, bool is_eos); - - /* This function can perform various checks on the connection. See - CONNCHECK_* for more information about the checks that can be performed, - and CONNRESULT_* for the results that can be returned. */ - uint32_t (*connection_check)(struct Curl_easy *data, - struct connectdata *conn, - uint32_t checks_to_perform); - - /* attach() attaches this transfer to this connection */ - void (*attach)(struct Curl_easy *data, struct connectdata *conn); - - /* return CURLE_OK if a redirect to `newurl` should be followed, - CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change - the way the follow request is performed. */ - CURLcode (*follow)(struct Curl_easy *data, const char *newurl, - followtype type); -}; - -struct Curl_scheme { - const char *name; /* URL scheme name in lowercase */ - const struct Curl_protocol *run; /* implementation */ - curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single - specific protocol bit */ - curl_prot_t family; /* single bit for protocol family; the non-TLS name - of the protocol this is */ - uint32_t flags; /* Extra particular characteristics, see PROTOPT_* */ - uint16_t defport; /* Default port. */ -}; - -#define PROTOPT_NONE 0 /* nothing extra */ -#define PROTOPT_SSL (1 << 0) /* uses SSL */ -#define PROTOPT_DUAL (1 << 1) /* this protocol uses two connections */ -#define PROTOPT_CLOSEACTION (1 << 2) /* need action before socket close */ -/* some protocols will have to call the underlying functions without regard to - what exact state the socket signals. IE even if the socket says "readable", - the send function might need to be called while uploading, or vice versa. -*/ -#define PROTOPT_DIRLOCK (1 << 3) -#define PROTOPT_NONETWORK (1 << 4) /* protocol does not use the network! */ -#define PROTOPT_NEEDSPWD (1 << 5) /* needs a password, and if none is set it - gets a default */ -#define PROTOPT_NOURLQUERY (1 << 6) /* protocol cannot handle - URL query strings (?foo=bar) ! */ -#define PROTOPT_CREDSPERREQUEST (1 << 7) /* requires login credentials per - request instead of per - connection */ -#define PROTOPT_ALPN (1 << 8) /* set ALPN for this */ -/* (1 << 9) was PROTOPT_STREAM, now free */ -#define PROTOPT_URLOPTIONS (1 << 10) /* allow options part in the userinfo - field of the URL */ -#define PROTOPT_PROXY_AS_HTTP (1 << 11) /* allow this non-HTTP scheme over a - HTTP proxy as HTTP proxies may know - this protocol and act as - a gateway */ -#define PROTOPT_WILDCARD (1 << 12) /* protocol supports wildcard matching */ -#define PROTOPT_USERPWDCTRL (1 << 13) /* Allow "control bytes" (< 32 ASCII) in - username and password */ -#define PROTOPT_NOTCPPROXY (1 << 14) /* this protocol cannot proxy over TCP */ -#define PROTOPT_SSL_REUSE (1 << 15) /* this protocol may reuse an existing - SSL connection in the same family - without having PROTOPT_SSL. */ -#define PROTOPT_CONN_REUSE (1 << 16) /* this protocol can reuse connections */ - -#define CONNCHECK_NONE 0 /* No checks */ -#define CONNCHECK_ISDEAD (1 << 0) /* Check if the connection is dead. */ -#define CONNCHECK_KEEPALIVE (1 << 1) /* Perform any keepalive function. */ - -#define CONNRESULT_NONE 0 /* No extra information. */ -#define CONNRESULT_DEAD (1 << 0) /* The connection is dead. */ - #define TRNSPRT_NONE 0 #define TRNSPRT_TCP 3 #define TRNSPRT_UDP 4 @@ -630,7 +383,7 @@ char *sasl_authzid; /* authorization identity string, allocated */ char *oauth_bearer; /* OAUTH2 bearer, allocated */ struct curltime created; /* creation time */ - struct curltime lastused; /* when returned to the connection poolas idle */ + struct curltime lastused; /* when returned to the connection pool as idle */ /* A connection can have one or two sockets and connection filters. * The protocol using the 2nd one is FTP for CONTROL+DATA sockets */ @@ -695,33 +448,31 @@ that subsequent bound-requested connections are not accidentally reusing wrong connections. */ char *localdev; - uint16_t localportrange; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) int socks5_gssapi_enctype; #endif - /* The field below gets set in connect.c:connecthost() */ - int remote_port; /* the remote port, not the proxy port! */ - int conn_to_port; /* the remote port to connect to. valid only if - bits.conn_to_port is set */ - uint32_t attached_xfers; /* # of attached easy handles */ #ifdef USE_IPV6 uint32_t scope_id; /* Scope id for IPv6 */ #endif + /* The field below gets set in connect.c:connecthost() */ + uint16_t remote_port; /* the remote port, not the proxy port! */ + uint16_t conn_to_port; /* the remote port to connect to. valid only if + bits.conn_to_port is set */ + uint16_t localportrange; uint16_t localport; uint16_t secondary_port; /* secondary socket remote port to connect to (ftp) */ - uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not - necessarily the transport the connection ends using due to Alt-Svc - and happy eyeballing. Use `Curl_conn_get_transport() for actual value - once the connection is set up. */ + uint8_t transport_wanted; /* one of the TRNSPRT_* defines. Not necessarily + the transport the connection ends using due to Alt-Svc and happy + eyeballing. Use Curl_conn_get_transport() for actual value once the + connection is set up. */ uint8_t ip_version; /* copied from the Curl_easy at creation time */ /* HTTP version last responded with by the server or negotiated via ALPN. * 0 at start, then one of 09, 10, 11, etc. */ uint8_t httpversion_seen; uint8_t gssapi_delegation; /* inherited from set.gssapi_delegation */ - BIT(connect_only); }; #ifndef CURL_DISABLE_PROXY @@ -792,6 +543,7 @@ force redraw at next call */ struct pgrs_dir ul; struct pgrs_dir dl; + curl_off_t deliver; /* amount of data delivered to application */ curl_off_t current_speed; /* uses the currently fastest transfer */ curl_off_t earlydata_sent; @@ -817,7 +569,7 @@ curl_off_t speed_amount[CURL_SPEED_RECORDS]; struct curltime speed_time[CURL_SPEED_RECORDS]; - uint8_t speeder_c; + uint32_t speeder_c; BIT(hide); BIT(ul_size_known); BIT(dl_size_known); @@ -899,13 +651,6 @@ EXPIRE_LAST /* not an actual timer, used as a marker only */ } expire_id; -typedef enum { - TRAILERS_NONE, - TRAILERS_INITIALIZED, - TRAILERS_SENDING, - TRAILERS_DONE -} trailers_state; - /* * One instance for each timeout an easy handle can set. */ @@ -940,8 +685,10 @@ curl_off_t recent_conn_id; /* The most recent connection used, might no * longer exist */ struct dynbuf headerb; /* buffer to store headers in */ +#ifndef CURL_DISABLE_HSTS struct curl_slist *hstslist; /* list of HSTS files set by curl_easy_setopt(HSTS) calls */ +#endif curl_off_t current_speed; /* the ProgressShow() function sets this, bytes / second */ @@ -965,9 +712,8 @@ struct auth authhost; /* auth details for host */ struct auth authproxy; /* auth details for proxy */ - struct Curl_dns_entry *dns[2]; /* DNS to connect FIRST/SECONDARY */ #ifdef USE_CURL_ASYNC - struct Curl_async async; /* asynchronous name resolver data */ + struct Curl_resolv_async *async; /* asynchronous name resolver data */ #endif #ifdef USE_OPENSSL @@ -1027,8 +773,6 @@ struct Curl_llist httphdrs; /* received headers */ struct curl_header headerout[2]; /* for external purposes */ struct Curl_header_store *prevhead; /* the latest added header */ - trailers_state trailers_state; /* whether we are sending trailers - and what stage are we at */ #endif #ifndef CURL_DISABLE_COOKIES struct curl_slist *cookielist; /* list of cookie files set by @@ -1048,13 +792,9 @@ struct dynamically_allocated_data { char *uagent; char *accept_encoding; - char *userpwd; char *rangeline; char *ref; char *host; -#ifndef CURL_DISABLE_COOKIES - char *cookiehost; -#endif #ifndef CURL_DISABLE_RTSP char *rtsp_transport; #endif @@ -1063,7 +803,6 @@ char *user; char *passwd; #ifndef CURL_DISABLE_PROXY - char *proxyuserpwd; char *proxyuser; char *proxypasswd; #endif
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c index 9609390..5ecfd60 100644 --- a/Utilities/cmcurl/lib/vauth/digest.c +++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -255,13 +255,13 @@ * Parameters: * * chlgref [in] - The challenge message. - * nonce [in/out] - The buffer where the nonce will be stored. + * nonce [in/out] - The buffer where the nonce is stored. * nlen [in] - The length of the nonce buffer. - * realm [in/out] - The buffer where the realm will be stored. + * realm [in/out] - The buffer where the realm is stored. * rlen [in] - The length of the realm buffer. - * alg [in/out] - The buffer where the algorithm will be stored. + * alg [in/out] - The buffer where the algorithm is stored. * alen [in] - The length of the algorithm buffer. - * qop [in/out] - The buffer where the qop-options will be stored. + * qop [in/out] - The buffer where the qop-options is stored. * qlen [in] - The length of the qop buffer. * * Returns CURLE_OK on success. @@ -356,6 +356,7 @@ char *spn = NULL; char *qrealm; char *qnonce; + char *quserp; /* Decode the challenge message */ CURLcode result = auth_decode_digest_md5_message(chlg, @@ -384,7 +385,7 @@ if(result) return result; - /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ + /* Good so far, now calculate A1 and H(A1) according to RFC 2831 */ ctxt = Curl_MD5_init(&Curl_DIGEST_MD5); if(!ctxt) return CURLE_OUT_OF_MEMORY; @@ -469,20 +470,22 @@ for(i = 0; i < MD5_DIGEST_LEN; i++) curl_msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); - /* escape double quotes and backslashes in the realm and nonce as + /* escape double quotes and backslashes in the username, realm and nonce as necessary */ qrealm = auth_digest_string_quoted(realm); qnonce = auth_digest_string_quoted(nonce); - if(qrealm && qnonce) + quserp = auth_digest_string_quoted(userp); + if(qrealm && qnonce && quserp) /* Generate the response */ response = curl_maprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\"," "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\"," "response=%s,qop=%s", - userp, qrealm, qnonce, + quserp, qrealm, qnonce, cnonce, nonceCount, spn, resp_hash_hex, qop); curlx_free(qrealm); curlx_free(qnonce); + curlx_free(quserp); curlx_free(spn); if(!response) return CURLE_OUT_OF_MEMORY; @@ -669,7 +672,7 @@ * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -853,8 +856,8 @@ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" Digest parameters are all quoted strings. Username which is provided by - the user will need double quotes and backslashes within it escaped. - realm, nonce, and opaque will need backslashes as well as they were + the user needs double quotes and backslashes within it escaped. + realm, nonce, and opaque needs backslashes as well as they were de-escaped when copied from request header. cnonce is generated with web-safe characters. uri is already percent encoded. nc is 8 hex characters. algorithm and qop with standard values only contain web-safe @@ -977,7 +980,7 @@ * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -1028,12 +1031,12 @@ */ void Curl_auth_digest_cleanup(struct digestdata *digest) { - Curl_safefree(digest->nonce); - Curl_safefree(digest->cnonce); - Curl_safefree(digest->realm); - Curl_safefree(digest->opaque); - Curl_safefree(digest->qop); - Curl_safefree(digest->algorithm); + curlx_safefree(digest->nonce); + curlx_safefree(digest->cnonce); + curlx_safefree(digest->realm); + curlx_safefree(digest->opaque); + curlx_safefree(digest->qop); + curlx_safefree(digest->algorithm); digest->nc = 0; digest->algo = ALGO_MD5; /* default algorithm */
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c index f29e569..f0b6780 100644 --- a/Utilities/cmcurl/lib/vauth/digest_sspi.c +++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -375,7 +375,7 @@ * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -427,10 +427,10 @@ (passwdp && digest->passwd && Curl_timestrcmp(passwdp, digest->passwd))) { if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); } if(digest->http_context) { @@ -460,7 +460,7 @@ else { /* delete the context so a new one can be made */ infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx", status); Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } } @@ -474,8 +474,8 @@ TCHAR *spn; /* free the copy of user/passwd used to make the previous identity */ - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); if(userp && *userp) { /* Populate our identity structure */ @@ -515,7 +515,7 @@ if(!digest->passwd) { curlx_free(output_token); Curl_sspi_free_identity(p_identity); - Curl_safefree(digest->user); + curlx_safefree(digest->user); return CURLE_OUT_OF_MEMORY; } } @@ -595,7 +595,7 @@ Curl_sspi_free_identity(p_identity); curlx_free(output_token); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); if(status == SEC_E_INSUFFICIENT_MEMORY) return CURLE_OUT_OF_MEMORY; @@ -637,7 +637,7 @@ void Curl_auth_digest_cleanup(struct digestdata *digest) { /* Free the input token */ - Curl_safefree(digest->input_token); + curlx_safefree(digest->input_token); /* Reset any variables */ digest->input_token_len = 0; @@ -645,12 +645,12 @@ /* Delete security context */ if(digest->http_context) { Curl_pSecFn->DeleteSecurityContext(digest->http_context); - Curl_safefree(digest->http_context); + curlx_safefree(digest->http_context); } /* Free the copy of user/passwd used to make the identity for http_context */ - Curl_safefree(digest->user); - Curl_safefree(digest->passwd); + curlx_safefree(digest->user); + curlx_safefree(digest->passwd); } #endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */
diff --git a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c index d6af18f..64c735b 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_gssapi.c
@@ -33,7 +33,7 @@ #include "curl_gssapi.h" #include "curl_trc.h" -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -320,7 +320,7 @@ } } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/vauth/krb5_sspi.c b/Utilities/cmcurl/lib/vauth/krb5_sspi.c index c08b6a4..e7491be 100644 --- a/Utilities/cmcurl/lib/vauth/krb5_sspi.c +++ b/Utilities/cmcurl/lib/vauth/krb5_sspi.c
@@ -444,8 +444,8 @@ krb5->p_identity = NULL; /* Free the SPN and output token */ - Curl_safefree(krb5->spn); - Curl_safefree(krb5->output_token); + curlx_safefree(krb5->spn); + curlx_safefree(krb5->output_token); /* Reset any variables */ krb5->token_max = 0;
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c index b50cfdd..1e485ed 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm.c +++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -152,7 +152,7 @@ /* Indicates that 128-bit encryption is supported. */ #define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1 << 30) -/* Indicates that the client will provide an encrypted master key in +/* Indicates that the client provides an encrypted master key in the "Session Key" field of the Type 3 message. */ #define NTLMFLAG_NEGOTIATE_56 (1 << 31) @@ -424,7 +424,7 @@ const char *userp, const char *passwdp, const char *service, - const char *hostname, + const char *host, struct ntlmdata *ntlm, struct bufref *out) { @@ -445,7 +445,7 @@ size_t size; char *ntlmbuf; - const char *host = ""; /* empty */ + const char *hostname = ""; /* empty */ const char *domain = ""; /* empty */ size_t hostlen = 0; size_t domlen = 0; @@ -456,7 +456,7 @@ (void)userp; (void)passwdp; (void)service; - (void)hostname; + (void)host; /* Clean up any former leftovers and initialise to defaults */ Curl_auth_cleanup_ntlm(ntlm); @@ -490,7 +490,7 @@ SHORTPAIR(hostlen), SHORTPAIR(hostoff), 0, 0, - host, /* this is empty */ + hostname, /* this is empty */ domain /* this is empty */); if(!ntlmbuf) @@ -850,7 +850,7 @@ void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) { /* Free the target info */ - Curl_safefree(ntlm->target_info); + curlx_safefree(ntlm->target_info); /* Reset any variables */ ntlm->target_info_len = 0;
diff --git a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c index 12cf32b..4c41eb2 100644 --- a/Utilities/cmcurl/lib/vauth/ntlm_sspi.c +++ b/Utilities/cmcurl/lib/vauth/ntlm_sspi.c
@@ -191,21 +191,21 @@ * Returns CURLE_OK on success. */ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, - const struct bufref *type2, + const struct bufref *type2ref, struct ntlmdata *ntlm) { /* Ensure we have a valid type-2 message */ - if(!Curl_bufref_len(type2)) { + if(!Curl_bufref_len(type2ref)) { infof(data, "NTLM handshake failure (empty type-2 message)"); return CURLE_BAD_CONTENT_ENCODING; } /* Store the challenge for later use */ - ntlm->input_token = curlx_memdup0(Curl_bufref_ptr(type2), - Curl_bufref_len(type2)); + ntlm->input_token = curlx_memdup0(Curl_bufref_ptr(type2ref), + Curl_bufref_len(type2ref)); if(!ntlm->input_token) return CURLE_OUT_OF_MEMORY; - ntlm->input_token_len = Curl_bufref_len(type2); + ntlm->input_token_len = Curl_bufref_len(type2ref); return CURLE_OK; } @@ -255,7 +255,7 @@ /* ssl context comes from schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS will not pass the authentication (401 response). + * otherwise IIS does not pass the authentication (401 response). * Minimum supported version is Windows 7. * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 */ @@ -341,13 +341,13 @@ ntlm->p_identity = NULL; /* Free the input and output tokens */ - Curl_safefree(ntlm->input_token); - Curl_safefree(ntlm->output_token); + curlx_safefree(ntlm->input_token); + curlx_safefree(ntlm->output_token); /* Reset any variables */ ntlm->token_max = 0; - Curl_safefree(ntlm->spn); + curlx_safefree(ntlm->spn); } #endif /* USE_WINDOWS_SSPI && USE_NTLM */
diff --git a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c index af8498b..38bb4c1 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_gssapi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_gssapi.c
@@ -32,7 +32,7 @@ #include "curl_gssapi.h" #include "curl_trc.h" -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif @@ -171,7 +171,7 @@ NULL); /* Free the decoded challenge as it is not required anymore */ - Curl_safefree(input_token.value); + curlx_safefree(input_token.value); nego->status = major_status; if(GSS_ERROR(major_status)) { @@ -211,7 +211,7 @@ * data [in] - The session handle. * nego [in/out] - The Negotiate data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -288,7 +288,7 @@ nego->havemultiplerequests = FALSE; } -#if defined(__GNUC__) && defined(__APPLE__) +#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #pragma GCC diagnostic pop #endif
diff --git a/Utilities/cmcurl/lib/vauth/spnego_sspi.c b/Utilities/cmcurl/lib/vauth/spnego_sspi.c index 1f73123..1baf593 100644 --- a/Utilities/cmcurl/lib/vauth/spnego_sspi.c +++ b/Utilities/cmcurl/lib/vauth/spnego_sspi.c
@@ -192,7 +192,7 @@ /* ssl context comes from Schannel. * When extended protection is used in IIS server, * we have to pass a second SecBuffer to the SecBufferDesc - * otherwise IIS will not pass the authentication (401 response). + * otherwise IIS does not pass the authentication (401 response). * Minimum supported version is Windows 7. * https://learn.microsoft.com/security-updates/SecurityAdvisories/2009/973811 */ @@ -278,7 +278,7 @@ * data [in] - The session handle. * nego [in/out] - The Negotiate data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory - * holding the result will be stored upon completion. + * holding the result is stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. @@ -329,8 +329,8 @@ nego->p_identity = NULL; /* Free the SPN and output token */ - Curl_safefree(nego->spn); - Curl_safefree(nego->output_token); + curlx_safefree(nego->spn); + curlx_safefree(nego->output_token); /* Reset any variables */ nego->status = 0;
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h index 3e66c89..279da60 100644 --- a/Utilities/cmcurl/lib/vauth/vauth.h +++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -117,6 +117,7 @@ /* This is used to clean up the digest specific data */ void Curl_auth_digest_cleanup(struct digestdata *digest); #else +#define Curl_auth_digest_cleanup(x) #define Curl_auth_is_digest_supported() FALSE #endif /* !CURL_DISABLE_DIGEST_AUTH */ @@ -199,7 +200,7 @@ const char *userp, const char *passwdp, const char *service, - const char *hostname, + const char *host, struct ntlmdata *ntlm, struct bufref *out); @@ -263,7 +264,7 @@ const char *passwdp, const char *service, const char *host, - const bool mutual, + const bool mutual_auth, const struct bufref *chlg, struct kerberos5data *krb5, struct bufref *out);
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c index ff9aed0..a3e69ac 100644 --- a/Utilities/cmcurl/lib/version.c +++ b/Utilities/cmcurl/lib/version.c
@@ -46,23 +46,18 @@ #include <libpsl.h> #endif -#ifdef USE_LIBRTMP -#include <librtmp/rtmp.h> -#include "curl_rtmp.h" -#endif - #ifdef HAVE_LIBZ #include <cm3p/zlib.h> #endif #ifdef HAVE_BROTLI -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG /* Ignore -Wvla warnings in brotli headers */ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvla" #endif #include <brotli/decode.h> -#if defined(__GNUC__) || defined(__clang__) +#ifdef CURL_HAVE_DIAG #pragma GCC diagnostic pop #endif #endif @@ -178,9 +173,6 @@ #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) char h3_version[30]; #endif -#ifdef USE_LIBRTMP - char rtmp_version[30]; -#endif #ifdef USE_GSASL char gsasl_buf[30]; #endif @@ -244,10 +236,6 @@ Curl_quic_ver(h3_version, sizeof(h3_version)); src[i++] = h3_version; #endif -#ifdef USE_LIBRTMP - Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); - src[i++] = rtmp_version; -#endif #ifdef USE_GSASL curl_msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s", gsasl_check_version(NULL)); @@ -255,11 +243,9 @@ #endif #ifdef HAVE_GSSAPI #ifdef HAVE_GSSGNU - curl_msnprintf(gss_buf, sizeof(gss_buf), "libgss/%s", - GSS_VERSION); + curl_msnprintf(gss_buf, sizeof(gss_buf), "libgss/%s", GSS_VERSION); #elif defined(CURL_KRB5_VERSION) - curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5/%s", - CURL_KRB5_VERSION); + curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5/%s", CURL_KRB5_VERSION); #else curl_msnprintf(gss_buf, sizeof(gss_buf), "mit-krb5"); #endif @@ -350,14 +336,6 @@ #if defined(USE_SSL) && !defined(CURL_DISABLE_POP3) "pop3s", #endif -#ifdef USE_LIBRTMP - "rtmp", - "rtmpe", - "rtmps", - "rtmpt", - "rtmpte", - "rtmpts", -#endif #ifndef CURL_DISABLE_RTSP "rtsp", #endif @@ -365,7 +343,7 @@ "scp", "sftp", #endif -#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) +#if defined(CURL_ENABLE_SMB) && defined(USE_CURL_NTLM_CORE) "smb", # ifdef USE_SSL "smbs", @@ -451,8 +429,8 @@ #ifndef CURL_DISABLE_ALTSVC FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC), #endif -#if defined(USE_ARES) && defined(CURLRES_THREADED) && defined(USE_HTTPSRR) - FEATURE("asyn-rr", NULL, 0), +#if defined(USE_ARES) && defined(USE_RESOLV_THREADED) && defined(USE_HTTPSRR) + FEATURE("asyn-rr", NULL, 0), #endif #ifdef CURLRES_ASYNCH FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS), @@ -694,13 +672,5 @@ feature_names[n] = NULL; /* Terminate array. */ version_info.features = features; -#ifdef USE_LIBRTMP - { - static char rtmp_version[30]; - Curl_rtmp_version(rtmp_version, sizeof(rtmp_version)); - version_info.rtmp_version = rtmp_version; - } -#endif - return &version_info; }
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c index 48905d3..8cf3886 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -52,6 +52,7 @@ #include "rand.h" #include "multiif.h" #include "cfilters.h" +#include "cf-dns.h" #include "cf-socket.h" #include "connect.h" #include "progress.h" @@ -64,7 +65,6 @@ #include "vquic/vquic.h" #include "vquic/vquic_int.h" #include "vquic/vquic-tls.h" -#include "vtls/keylog.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" #include "vquic/curl_ngtcp2.h" @@ -183,7 +183,7 @@ struct cf_ngtcp2_ctx *ctx = cf->ctx; const ngtcp2_transport_params *rp; /* Peer should have sent us its transport parameters. If it - * announces a positive `max_idle_timeout` it will close the + * announces a positive `max_idle_timeout` it closes the * connection when it does not hear from us for that time. * * Some servers use this as a keep-alive timer at a rather low @@ -789,6 +789,7 @@ } } +/* for ngtcp2 <v1.22.0 */ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, uint8_t *token, size_t cidlen, void *user_data) @@ -809,6 +810,27 @@ return 0; } +#ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ +static int cb_get_new_connection_id2(ngtcp2_conn *tconn, ngtcp2_cid *cid, + struct ngtcp2_stateless_reset_token *token, size_t cidlen, void *user_data) +{ + CURLcode result; + (void)tconn; + (void)user_data; + + result = Curl_rand(NULL, cid->data, cidlen); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + cid->datalen = cidlen; + + result = Curl_rand(NULL, token->data, sizeof(token->data)); + if(result) + return NGTCP2_ERR_CALLBACK_FAILURE; + + return 0; +} +#endif + static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, void *user_data) { @@ -852,7 +874,7 @@ cb_extend_max_local_streams_bidi, NULL, /* extend_max_local_streams_uni */ cb_rand, - cb_get_new_connection_id, + cb_get_new_connection_id, /* for ngtcp2 <v1.22.0 */ NULL, /* remove_connection_id */ ngtcp2_crypto_update_key_cb, /* update_key */ NULL, /* path_validation */ @@ -878,6 +900,12 @@ #ifdef NGTCP2_CALLBACKS_V2 /* ngtcp2 v1.14.0+ */ NULL, /* begin_path_validation */ #endif +#ifdef NGTCP2_CALLBACKS_V3 /* ngtcp2 v1.22.0+ */ + NULL, /* recv_stateless_reset2 */ + cb_get_new_connection_id2, /* get_new_connection_id2 */ + NULL, /* dcid_status2 */ + ngtcp2_crypto_get_path_challenge_data2_cb, /* get_path_challenge_data2 */ +#endif }; #if defined(_MSC_VER) && defined(_DLL) @@ -1113,7 +1141,7 @@ if(stream->rx_offset_max < stream->rx_offset) stream->rx_offset_max = stream->rx_offset; - CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRId64, + CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, rx win=%" PRIu64, stream->id, blen, stream->rx_offset_max - stream->rx_offset); cf_ngtcp2_upd_rx_win(cf, data, stream); return 0; @@ -1461,8 +1489,8 @@ result = CURLE_AGAIN; out: - result = Curl_1st_err(result, cf_progress_egress(cf, data, &pktx)); - result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); + result = Curl_1st_fatal(result, cf_progress_egress(cf, data, &pktx)); + result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(blen=%zu) -> %d, %zu", stream ? stream->id : -1, blen, result, *pnread); @@ -1559,7 +1587,7 @@ } CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " - "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")", + "%d vecs%s with %zd (buffered=%zu, left=%" FMT_OFF_T ")", stream->id, (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "", nwritten, Curl_bufq_len(&stream->sendbuf), @@ -1788,7 +1816,7 @@ result = cf_progress_egress(cf, data, &pktx); out: - result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); + result = Curl_1st_fatal(result, check_and_set_expiry(cf, data, &pktx)); denied: CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : -1, len, result, *pnwritten); @@ -2018,7 +2046,7 @@ * This is called PMTUD (Path Maximum Transmission Unit Discovery). * Since a PMTUD might be rejected right on send, we do not want it * be followed by other packets of lesser size. Because those would - * also fail then. So, if we detect a PMTUD while buffering, we flush. + * also fail then. If we detect a PMTUD while buffering, we flush. */ max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); path_max_payload_size = @@ -2043,7 +2071,7 @@ ++pktcnt; if(pktcnt == 1) { /* first packet in buffer. This is either of a known, "good" - * payload size or it is a PMTUD. We will see. */ + * payload size or it is a PMTUD. We shall see. */ gsolen = nread; } else if(nread > gsolen || @@ -2216,8 +2244,8 @@ (uint8_t *)buffer, sizeof(buffer), &ctx->last_error, pktx.ts); CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%" - PRIu64 ") -> %d", ctx->last_error.type, - ctx->last_error.error_code, (int)nwritten); + PRIu64 ") -> %zd", ctx->last_error.type, + ctx->last_error.error_code, (ssize_t)nwritten); /* there are cases listed in ngtcp2 documentation where this call * may fail. Since we are doing a connection shutdown as graceful * as we can, such an error is ignored here. */ @@ -2257,7 +2285,7 @@ if(Curl_bufq_is_empty(&ctx->q.sendbuf)) { /* Sent everything off. ngtcp2 seems to have no support for graceful - * shutdowns. So, we are done. */ + * shutdowns. We are done. */ CURL_TRC_CF(data, cf, "shutdown completely sent off, done"); *done = TRUE; result = CURLE_OK; @@ -2384,7 +2412,7 @@ DEBUGASSERT(data); if(!data) return 0; - CURL_TRC_CF(data, cf, "SSL message: %s %s [%d]", + CURL_TRC_CF(data, cf, "SSL message: %s %s [%u]", incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype); switch(htype) { case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { @@ -2567,6 +2595,18 @@ return result; } +static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data) +{ +#ifdef USE_OPENSSL + return Curl_ossl_need_httpsrr(data); +#elif defined(USE_WOLFSSL) + return Curl_wssl_need_httpsrr(data); +#else + (void)data; + return FALSE; +#endif +} + /* * Might be called twice for happy eyeballs. */ @@ -2681,8 +2721,14 @@ } *done = FALSE; - pktx_init(&pktx, cf, data); + if(cf_ngtcp2_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } + + pktx_init(&pktx, cf, data); CF_DATA_SAVE(save, cf, data); if(!ctx->qconn) { @@ -2859,7 +2905,7 @@ goto out; /* We do not announce a max idle timeout, but when the peer does - * it will close the connection when it expires. */ + * it closes the connection when it expires. */ rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); if(rp && rp->max_idle_timeout) { timediff_t idletime_ms = @@ -2913,7 +2959,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai) + struct Curl_sockaddr_ex *addr) { struct cf_ngtcp2_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -2931,7 +2977,7 @@ goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn;
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h index 1a87faf..185272a 100644 --- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h +++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.h
@@ -53,7 +53,7 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai); + struct Curl_sockaddr_ex *addr); #endif #endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c index d8e6b30..73f664a 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.c +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -32,6 +32,7 @@ #include "uint-hash.h" #include "urldata.h" #include "cfilters.h" +#include "cf-dns.h" #include "cf-socket.h" #include "curl_trc.h" #include "rand.h" @@ -48,7 +49,6 @@ #include "url.h" #include "bufref.h" #include "vtls/openssl.h" -#include "vtls/keylog.h" #include "vtls/vtls.h" /* HTTP/3 error values defined in RFC 9114, ch. 8.1 */ @@ -481,7 +481,7 @@ return; /* Even when the transfer has already errored, we need to receive - * the data from quiche, as quiche will otherwise get stuck and + * the data from quiche, as quiche otherwise gets stuck and * raise events to receive over and over again. */ cb_ctx.cf = cf; cb_ctx.data = data; @@ -779,7 +779,7 @@ else failf(data, "connection closed by server"); /* Connection timed out, expire all transfers belonging to it - * as will not get any more POLL events here. */ + * as it does not get any more POLL events here. */ cf_quiche_expire_conn_closed(cf, data); return CURLE_SEND_ERROR; } @@ -918,7 +918,7 @@ result = CURLE_AGAIN; out: - result = Curl_1st_err(result, cf_flush_egress(cf, data)); + result = Curl_1st_fatal(result, cf_flush_egress(cf, data)); if(*pnread > 0) ctx->data_recvd += *pnread; CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_recv(len=%zu) -> %d, %zu, total=%" @@ -939,8 +939,8 @@ rv = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, (uint8_t *)CURL_UNCONST(buf), len, eos); if(rv == QUICHE_H3_ERR_DONE || (rv == 0 && len > 0)) { - /* Blocked on flow control and should HOLD sending. But when do we open - * again? */ + /* Blocked on flow control and should HOLD sending. + When do we open again? */ if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) { CURL_TRC_CF(data, cf, "[%" PRIu64 "] send_body(len=%zu) " "-> window exhausted", stream->id, len); @@ -1051,7 +1051,7 @@ goto out; } else { - CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRIu64, + CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64, Curl_bufref_ptr(&data->state.url), rv); } result = CURLE_SEND_ERROR; @@ -1144,7 +1144,7 @@ } out: - result = Curl_1st_err(result, cf_flush_egress(cf, data)); + result = Curl_1st_fatal(result, cf_flush_egress(cf, data)); CURL_TRC_CF(data, cf, "[%" PRIu64 "] cf_send(len=%zu) -> %d, %zu", stream ? stream->id : (uint64_t)~0, len, @@ -1152,8 +1152,8 @@ return result; } -static bool stream_is_writeable(struct Curl_cfilter *cf, - struct Curl_easy *data) +static bool stream_is_writable(struct Curl_cfilter *cf, + struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1181,7 +1181,7 @@ c_exhaust = FALSE; /* Have not found any call in quiche that tells us if the connection itself is blocked */ s_exhaust = want_send && stream && stream->opened && - (stream->quic_flow_blocked || !stream_is_writeable(cf, data)); + (stream->quic_flow_blocked || !stream_is_writable(cf, data)); want_recv = (want_recv || c_exhaust || s_exhaust); want_send = (!s_exhaust && want_send) || !Curl_bufq_is_empty(&ctx->q.sendbuf); @@ -1379,6 +1379,12 @@ } *done = FALSE; + if(Curl_ossl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } + vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data)); if(!ctx->qconn) { @@ -1634,7 +1640,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai) + struct Curl_sockaddr_ex *addr) { struct cf_quiche_ctx *ctx = NULL; struct Curl_cfilter *cf = NULL; @@ -1652,7 +1658,7 @@ goto out; cf->conn = conn; - result = Curl_cf_udp_create(&cf->next, data, conn, ai, TRNSPRT_QUIC); + result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC); if(result) goto out; cf->next->conn = cf->conn;
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.h b/Utilities/cmcurl/lib/vquic/curl_quiche.h index fb5a563..c2c88dd 100644 --- a/Utilities/cmcurl/lib/vquic/curl_quiche.h +++ b/Utilities/cmcurl/lib/vquic/curl_quiche.h
@@ -38,7 +38,7 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai); + struct Curl_sockaddr_ex *addr); #endif
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.c b/Utilities/cmcurl/lib/vquic/vquic-tls.c index e135ac5..d81f2a9 100644 --- a/Utilities/cmcurl/lib/vquic/vquic-tls.c +++ b/Utilities/cmcurl/lib/vquic/vquic-tls.c
@@ -45,7 +45,6 @@ #include "urldata.h" #include "cfilters.h" -#include "vtls/keylog.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" #include "vquic/vquic-tls.h"
diff --git a/Utilities/cmcurl/lib/vquic/vquic-tls.h b/Utilities/cmcurl/lib/vquic/vquic-tls.h index 33adec2..c461c15 100644 --- a/Utilities/cmcurl/lib/vquic/vquic-tls.h +++ b/Utilities/cmcurl/lib/vquic/vquic-tls.h
@@ -72,7 +72,7 @@ * @param ctx the TLS context to initialize * @param cf the connection filter involved * @param data the transfer involved - * @param peer the peer that will be connected to + * @param peer the peer to be connected to * @param alpns the ALPN specifications to negotiate, may be NULL * @param cb_setup optional callback for early TLS config * @param cb_user_data user_data param for callback
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index 1eaabb9..0040ad6 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -702,20 +702,20 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport) { (void)transport; DEBUGASSERT(transport == TRNSPRT_QUIC); #if defined(USE_NGTCP2) && defined(USE_NGHTTP3) - return Curl_cf_ngtcp2_create(pcf, data, conn, ai); + return Curl_cf_ngtcp2_create(pcf, data, conn, addr); #elif defined(USE_QUICHE) - return Curl_cf_quiche_create(pcf, data, conn, ai); + return Curl_cf_quiche_create(pcf, data, conn, addr); #else *pcf = NULL; (void)data; (void)conn; - (void)ai; + (void)addr; return CURLE_NOT_BUILT_IN; #endif } @@ -725,7 +725,7 @@ unsigned char transport) { if(transport == TRNSPRT_UNIX) { - /* cannot do QUIC over a Unix domain socket */ + failf(data, "HTTP/3 cannot be used over UNIX domain sockets"); return CURLE_QUIC_CONNECT_ERROR; } if(!(conn->scheme->flags & PROTOPT_SSL)) {
diff --git a/Utilities/cmcurl/lib/vquic/vquic.h b/Utilities/cmcurl/lib/vquic/vquic.h index 528e184..1f0a1ab 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.h +++ b/Utilities/cmcurl/lib/vquic/vquic.h
@@ -42,7 +42,7 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, - const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, uint8_t transport); extern struct Curl_cftype Curl_cft_http3;
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c index 3a2a52e..44094e4 100644 --- a/Utilities/cmcurl/lib/vssh/libssh.c +++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -147,7 +147,7 @@ rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, &hash, &hlen); if(rc != SSH_OK || hlen != 16) { failf(data, - "Denied establishing ssh session: md5 fingerprint not available"); + "Denied establishing ssh session: MD5 fingerprint not available"); goto cleanup; } @@ -158,7 +158,7 @@ if(!curl_strequal(md5buffer, pubkey_md5)) { failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " + "Denied establishing ssh session: mismatch MD5 fingerprint. " "Remote %s is not equal to %s", md5buffer, pubkey_md5); rc = SSH_ERROR; goto cleanup; @@ -357,6 +357,8 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); Curl_pgrsSetDownloadSize(data, -1); if(data->req.no_body) { myssh_to(data, sshc, SSH_STOP); @@ -384,6 +386,8 @@ { CURLcode result = CURLE_OK; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); curlx_dyn_reset(&sshc->readdir_buf); if(sshc->readdir_attrs) sftp_attributes_free(sshc->readdir_attrs); @@ -480,7 +484,7 @@ sshc->readdir_longentry = sshc->readdir_link_attrs->longname; } - Curl_safefree(sshc->readdir_linkPath); + curlx_safefree(sshc->readdir_linkPath); if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s", sshc->readdir_filename)) { /* Not using: @@ -519,8 +523,10 @@ ssh_string_free_char(sshc->readdir_tmp); sshc->readdir_tmp = NULL; - if(result) + if(result) { myssh_to(data, sshc, SSH_STOP); + sshc->actualcode = result; + } else myssh_to(data, sshc, SSH_SFTP_READDIR); return SSH_NO_ERROR; @@ -544,8 +550,8 @@ if(cmd) failf(data, "%s command failed: %s", cmd, ssh_get_error(sshc->ssh_session)); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -564,6 +570,8 @@ else if(statvfs) { #ifdef _MSC_VER #define CURL_LIBSSH_VFS_SIZE_MASK "I64u" +#elif defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 6) +#define CURL_LIBSSH_VFS_SIZE_MASK "llu" #else #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 #endif @@ -696,8 +704,8 @@ int dir = ssh_get_poll_flags(sshc->ssh_session); /* translate the libssh define bits into our own bit defines */ sshc->waitfor = - ((dir & SSH_READ_PENDING) ? KEEP_RECV : 0) | - ((dir & SSH_WRITE_PENDING) ? KEEP_SEND : 0); + ((dir & SSH_READ_PENDING) ? REQ_IO_RECV : 0) | + ((dir & SSH_WRITE_PENDING) ? REQ_IO_SEND : 0); } else sshc->waitfor = 0; @@ -955,6 +963,9 @@ int flags; int rc = 0; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + if(data->state.resume_from) { sftp_attributes attrs; @@ -1084,7 +1095,7 @@ /* upload data */ Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->recv_idx = FIRSTSOCKET; /* since we do not really wait for anything at this point, we want the @@ -1101,6 +1112,8 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); /* Work on getting the specified file */ if(sshc->sftp_file) sftp_close(sshc->sftp_file); @@ -1205,7 +1218,7 @@ } Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; sshc->sftp_recv_state = 0; @@ -1218,11 +1231,13 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); if(sshc->sftp_file) { sftp_close(sshc->sftp_file); sshc->sftp_file = NULL; } - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); CURL_TRC_SSH(data, "SFTP DONE done"); @@ -1306,8 +1321,8 @@ /* This is the last step in the SFTP connect phase. Do note that while we get the homedir here, we get the "workingpath" in the DO action - since the homedir will remain the same between request but the - working path will not. */ + since the homedir remains the same between request but the + working path does not. */ CURL_TRC_SSH(data, "CONNECT phase done"); myssh_to(data, sshc, SSH_STOP); return SSH_NO_ERROR; @@ -1317,7 +1332,12 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { - CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + CURLcode result; + + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); if(result) { sshc->actualcode = result; myssh_to(data, sshc, SSH_STOP); @@ -1346,12 +1366,12 @@ return SSH_NO_ERROR; } -static int return_quote_error(struct Curl_easy *data, - struct ssh_conn *sshc) +static int quote_error(struct Curl_easy *data, + struct ssh_conn *sshc) { failf(data, "Suspicious data after the command line"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1371,9 +1391,12 @@ const char *cmd = sshc->quote_item->data; sshc->acceptfail = FALSE; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1460,14 +1483,14 @@ else failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: " "Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); sshc->quote_attrs = NULL; myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT); return SSH_NO_ERROR; @@ -1483,20 +1506,20 @@ failf(data, "Out of memory"); else failf(data, "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK); return SSH_NO_ERROR; } else if(!strncmp(cmd, "mkdir ", 6)) { if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); /* create directory */ myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR); return SSH_NO_ERROR; @@ -1511,42 +1534,42 @@ failf(data, "Out of memory"); else failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; return SSH_NO_ERROR; } if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME); return SSH_NO_ERROR; } else if(!strncmp(cmd, "rmdir ", 6)) { /* delete directory */ if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR); return SSH_NO_ERROR; } else if(!strncmp(cmd, "rm ", 3)) { if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK); return SSH_NO_ERROR; } #ifdef HAS_STATVFS_SUPPORT else if(!strncmp(cmd, "statvfs ", 8)) { if(*cp) - return return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS); return SSH_NO_ERROR; } #endif failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; @@ -1556,8 +1579,8 @@ static int myssh_in_SFTP_NEXT_QUOTE(struct Curl_easy *data, struct ssh_conn *sshc) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); sshc->quote_item = sshc->quote_item->next; @@ -1583,8 +1606,8 @@ sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1659,9 +1682,6 @@ myssh_quote_error(data, sshc, NULL); return SSH_NO_ERROR; } - if(date > UINT_MAX) - /* because the liubssh API cannot deal with a larger value */ - date = UINT_MAX; if(!strncmp(cmd, "atime", 5)) sshc->quote_attrs->atime = (uint32_t)date; else /* mtime */ @@ -1736,7 +1756,7 @@ data->req.maxdownload = bytecount; Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; myssh_to(data, sshc, SSH_STOP); @@ -1830,30 +1850,348 @@ sshc->pubkey = NULL; } - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); curlx_dyn_free(&sshc->readdir_buf); - Curl_safefree(sshc->readdir_linkPath); + curlx_safefree(sshc->readdir_linkPath); SSH_STRING_FREE_CHAR(sshc->homedir); sshc->initialised = FALSE; } } +static int myssh_in_SFTP_QUOTE_SETSTAT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, + sshc->quote_attrs); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "setstat"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_SYMLINK(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, + sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "symlink"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_MKDIR(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, + (mode_t)data->set.new_directory_perms); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "mkdir"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_RENAME(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, + sshc->quote_path2); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rename"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_RMDIR(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rmdir"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_UNLINK(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); + if(rc == SSH_AGAIN) + return rc; + if(rc && !sshc->acceptfail) { + myssh_quote_error(data, sshc, "rm"); + return rc; + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_GETINFO(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->set.get_filetime) + myssh_to(data, sshc, SSH_SFTP_FILETIME); + else + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_FILETIME(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + sftp_attributes attrs; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + attrs = sftp_stat(sshc->sftp_session, sshp->path); + if(attrs) { + data->info.filetime = attrs->mtime; + sftp_attributes_free(attrs); + } + + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_TRANS_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + if(data->state.upload) + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + else { + size_t path_len = strlen(sshp->path); + + if(path_len && sshp->path[path_len - 1] == '/') + myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); + else + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); + } + + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + if(strlen(sshp->path) > 1) { + sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + } + else { + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + } + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + + infof(data, "Creating directory '%s'", sshp->path); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); + return SSH_NO_ERROR; + } + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_CREATE_DIRS_MKDIR(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + int rc; + int err; + /* 'mode' - parameter is preliminary - default to 0644 */ + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + rc = sftp_mkdir(sshc->sftp_session, sshp->path, + (mode_t)data->set.new_directory_perms); + if(rc == SSH_AGAIN) + return rc; + *sshc->slash_pos = '/'; + ++sshc->slash_pos; + if(rc < 0) { + /* + * Abort if failure was not that the directory already + * exists or the permission was denied (creation might + * succeed further down the path) - retry on unspecific + * FAILURE also + */ + err = sftp_get_error(sshc->sftp_session); + if((err != SSH_FX_FILE_ALREADY_EXISTS) && + (err != SSH_FX_FAILURE) && + (err != SSH_FX_PERMISSION_DENIED)) { + return myssh_to_SFTP_CLOSE(data, sshc); + } + rc = 0; /* clear rc and continue */ + } + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + return rc; +} + +static int myssh_in_SCP_UPLOAD_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + int rc; + if(!sshp) + return myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); + + rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + rc = ssh_scp_push_file64(sshc->scp_session, sshp->path, + (uint64_t)data->state.infilesize, + (int)data->set.new_file_perms); + + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); + } + + /* upload data */ + Curl_xfer_setup_send(data, FIRSTSOCKET); + + /* not set by Curl_xfer_setup to preserve io_flags */ + data->conn->recv_idx = FIRSTSOCKET; + + myssh_to(data, sshc, SSH_STOP); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_DOWNLOAD_INIT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = ssh_scp_init(sshc->scp_session); + if(rc != SSH_OK) { + const char *err_msg = ssh_get_error(sshc->ssh_session); + failf(data, "%s", err_msg); + return myssh_to_ERROR(data, sshc, + CURLE_COULDNT_CONNECT); + } + myssh_to(data, sshc, SSH_SCP_DOWNLOAD); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_DONE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SCP_SEND_EOF); + else + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_SEND_EOF(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->scp_session) { + int rc = ssh_scp_close(sshc->scp_session); + if(rc == SSH_AGAIN) { + /* Currently the ssh_scp_close handles waiting for + * EOF in blocking way. + */ + return SSH_AGAIN; + } + if(rc != SSH_OK) { + infof(data, + "Failed to close libssh scp channel: %s", + ssh_get_error(sshc->ssh_session)); + } + } + + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return SSH_NO_ERROR; +} + +static int myssh_in_SCP_CHANNEL_FREE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->scp_session) { + ssh_scp_free(sshc->scp_session); + sshc->scp_session = NULL; + } + CURL_TRC_SSH(data, "SCP DONE phase complete"); + + ssh_set_blocking(sshc->ssh_session, 0); + + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); + return SSH_NO_ERROR; +} + +static CURLcode myssh_in_SESSION_FREE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + CURLcode result; + sshc_cleanup(sshc); + /* the code we are about to return */ + result = sshc->actualcode; + memset(sshc, 0, sizeof(struct ssh_conn)); + connclose(data->conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + sshc->nextstate = SSH_NO_STATE; + myssh_to(data, sshc, SSH_STOP); + return result; +} + /* - * ssh_statemach_act() runs the SSH state machine as far as it can without + * myssh_statemachine() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points - * to will be set to TRUE if the libssh function returns SSH_AGAIN + * to is set to TRUE if the libssh function returns SSH_AGAIN * meaning it wants to be called again when the socket is ready */ -static CURLcode myssh_statemach_act(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp, - bool *block) +static CURLcode myssh_statemachine(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) { CURLcode result = CURLE_OK; - int rc = SSH_NO_ERROR, err; + int rc = SSH_NO_ERROR; *block = FALSE; /* we are not blocking by default */ do { @@ -1909,14 +2247,13 @@ rc = myssh_in_SFTP_REALPATH(data, sshc); break; case SSH_SFTP_QUOTE_INIT: - rc = sshp ? myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp); break; case SSH_SFTP_POSTQUOTE_INIT: rc = myssh_in_SFTP_POSTQUOTE_INIT(data, sshc); break; case SSH_SFTP_QUOTE: - rc = sshp ? myssh_in_SFTP_QUOTE(data, sshc, sshp) : CURLE_FAILED_INIT; + rc = myssh_in_SFTP_QUOTE(data, sshc, sshp); break; case SSH_SFTP_NEXT_QUOTE: rc = myssh_in_SFTP_NEXT_QUOTE(data, sshc); @@ -1925,186 +2262,52 @@ rc = myssh_in_SFTP_QUOTE_STAT(data, sshc); break; case SSH_SFTP_QUOTE_SETSTAT: - rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, - sshc->quote_attrs); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - myssh_quote_error(data, sshc, "setstat"); - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_SETSTAT(data, sshc); break; - case SSH_SFTP_QUOTE_SYMLINK: - rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, - sshc->quote_path1); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - myssh_quote_error(data, sshc, "symlink"); - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_SYMLINK(data, sshc); break; - case SSH_SFTP_QUOTE_MKDIR: - rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, - (mode_t)data->set.new_directory_perms); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - myssh_quote_error(data, sshc, "mkdir"); - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_MKDIR(data, sshc); break; - case SSH_SFTP_QUOTE_RENAME: - rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, - sshc->quote_path2); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - myssh_quote_error(data, sshc, "rename"); - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_RENAME(data, sshc); break; - case SSH_SFTP_QUOTE_RMDIR: - rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - myssh_quote_error(data, sshc, "rmdir"); - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_RMDIR(data, sshc); break; - case SSH_SFTP_QUOTE_UNLINK: - rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); - if(rc == SSH_AGAIN) - break; - if(rc && !sshc->acceptfail) { - myssh_quote_error(data, sshc, "rm"); - break; - } - myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_UNLINK(data, sshc); break; - case SSH_SFTP_QUOTE_STATVFS: rc = myssh_in_SFTP_QUOTE_STATVFS(data, sshc); break; - case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - myssh_to(data, sshc, SSH_SFTP_FILETIME); - } - else { - myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); - } + rc = myssh_in_SFTP_GETINFO(data, sshc); break; - - case SSH_SFTP_FILETIME: { - sftp_attributes attrs; - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - attrs = sftp_stat(sshc->sftp_session, sshp->path); - if(attrs) { - data->info.filetime = attrs->mtime; - sftp_attributes_free(attrs); - } - - myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + case SSH_SFTP_FILETIME: + rc = myssh_in_SFTP_FILETIME(data, sshc, sshp); break; - } - case SSH_SFTP_TRANS_INIT: - if(data->state.upload) - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); - else if(sshp) { - if(sshp->path[strlen(sshp->path) - 1] == '/') - myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); - else - myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); - } - else - result = CURLE_FAILED_INIT; + rc = myssh_in_SFTP_TRANS_INIT(data, sshc, sshp); break; - case SSH_SFTP_UPLOAD_INIT: - rc = sshp ? myssh_in_UPLOAD_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_UPLOAD_INIT(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS_INIT: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - else if(strlen(sshp->path) > 1) { - sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); - } - else { - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); - } + rc = myssh_in_SFTP_CREATE_DIRS_INIT(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS: - sshc->slash_pos = strchr(sshc->slash_pos, '/'); - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - else if(sshc->slash_pos) { - *sshc->slash_pos = 0; - - infof(data, "Creating directory '%s'", sshp->path); - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); - break; - } - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + rc = myssh_in_SFTP_CREATE_DIRS(data, sshc, sshp); break; - case SSH_SFTP_CREATE_DIRS_MKDIR: - /* 'mode' - parameter is preliminary - default to 0644 */ - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - rc = sftp_mkdir(sshc->sftp_session, sshp->path, - (mode_t)data->set.new_directory_perms); - *sshc->slash_pos = '/'; - ++sshc->slash_pos; - if(rc < 0) { - /* - * Abort if failure was not that the directory already exists or - * the permission was denied (creation might succeed further down - * the path) - retry on unspecific FAILURE also - */ - err = sftp_get_error(sshc->sftp_session); - if((err != SSH_FX_FILE_ALREADY_EXISTS) && - (err != SSH_FX_FAILURE) && - (err != SSH_FX_PERMISSION_DENIED)) { - rc = myssh_to_SFTP_CLOSE(data, sshc); - break; - } - rc = 0; /* clear rc and continue */ - } - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + rc = myssh_in_SFTP_CREATE_DIRS_MKDIR(data, sshc, sshp); break; - case SSH_SFTP_READDIR_INIT: - rc = sshp ? myssh_in_SFTP_READDIR_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_READDIR_INIT(data, sshc, sshp); break; case SSH_SFTP_READDIR: - rc = sshp ? myssh_in_SFTP_READDIR(data, sshc, sshp) : CURLE_FAILED_INIT; + rc = myssh_in_SFTP_READDIR(data, sshc, sshp); break; case SSH_SFTP_READDIR_LINK: rc = myssh_in_SFTP_READDIR_LINK(data, sshc); @@ -2116,14 +2319,13 @@ rc = myssh_in_SFTP_READDIR_DONE(data, sshc); break; case SSH_SFTP_DOWNLOAD_INIT: - rc = sshp ? myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp) : - CURLE_FAILED_INIT; + rc = myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp); break; case SSH_SFTP_DOWNLOAD_STAT: rc = myssh_in_SFTP_DOWNLOAD_STAT(data, sshc); break; case SSH_SFTP_CLOSE: - rc = sshp ? myssh_in_SFTP_CLOSE(data, sshc, sshp) : CURLE_FAILED_INIT; + rc = myssh_in_SFTP_CLOSE(data, sshc, sshp); break; case SSH_SFTP_SHUTDOWN: rc = myssh_in_SFTP_SHUTDOWN(data, sshc); @@ -2131,107 +2333,32 @@ case SSH_SCP_TRANS_INIT: rc = myssh_in_TRANS_INIT(data, sshc, sshp); break; - case SSH_SCP_UPLOAD_INIT: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - rc = ssh_scp_init(sshc->scp_session); - if(rc != SSH_OK) { - const char *err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - rc = ssh_scp_push_file64(sshc->scp_session, sshp->path, - (uint64_t)data->state.infilesize, - (int)data->set.new_file_perms); - - if(rc != SSH_OK) { - const char *err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); - break; - } - - /* upload data */ - Curl_xfer_setup_send(data, FIRSTSOCKET); - - /* not set by Curl_xfer_setup to preserve keepon bits */ - data->conn->recv_idx = FIRSTSOCKET; - - myssh_to(data, sshc, SSH_STOP); - + rc = myssh_in_SCP_UPLOAD_INIT(data, sshc, sshp); break; - case SSH_SCP_DOWNLOAD_INIT: - rc = ssh_scp_init(sshc->scp_session); - if(rc != SSH_OK) { - const char *err_msg = ssh_get_error(sshc->ssh_session); - failf(data, "%s", err_msg); - rc = myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); + rc = myssh_in_SCP_DOWNLOAD_INIT(data, sshc); + if(rc) break; - } - myssh_to(data, sshc, SSH_SCP_DOWNLOAD); FALLTHROUGH(); - case SSH_SCP_DOWNLOAD: rc = myssh_SSH_SCP_DOWNLOAD(data, sshc); break; - case SSH_SCP_DONE: - if(data->state.upload) - myssh_to(data, sshc, SSH_SCP_SEND_EOF); - else - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + rc = myssh_in_SCP_DONE(data, sshc); break; - case SSH_SCP_SEND_EOF: - if(sshc->scp_session) { - rc = ssh_scp_close(sshc->scp_session); - if(rc == SSH_AGAIN) { - /* Currently the ssh_scp_close handles waiting for EOF in - * blocking way. - */ - break; - } - if(rc != SSH_OK) { - infof(data, "Failed to close libssh scp channel: %s", - ssh_get_error(sshc->ssh_session)); - } - } - - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + rc = myssh_in_SCP_SEND_EOF(data, sshc); break; - case SSH_SCP_CHANNEL_FREE: - if(sshc->scp_session) { - ssh_scp_free(sshc->scp_session); - sshc->scp_session = NULL; - } - CURL_TRC_SSH(data, "SCP DONE phase complete"); - - ssh_set_blocking(sshc->ssh_session, 0); - - myssh_to(data, sshc, SSH_SESSION_DISCONNECT); + myssh_in_SCP_CHANNEL_FREE(data, sshc); FALLTHROUGH(); - case SSH_SESSION_DISCONNECT: myssh_SESSION_DISCONNECT(data, sshc); FALLTHROUGH(); case SSH_SESSION_FREE: - sshc_cleanup(sshc); - /* the code we are about to return */ - result = sshc->actualcode; - memset(sshc, 0, sizeof(struct ssh_conn)); - connclose(data->conn, "SSH session free"); - sshc->state = SSH_SESSION_FREE; /* current */ - sshc->nextstate = SSH_NO_STATE; - myssh_to(data, sshc, SSH_STOP); + result = myssh_in_SESSION_FREE(data, sshc); break; - case SSH_QUIT: default: /* internal error */ @@ -2268,12 +2395,12 @@ if(!sshc || (sock == CURL_SOCKET_BAD)) return CURLE_FAILED_INIT; - waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon; + waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; if(waitfor) { int flags = 0; - if(waitfor & KEEP_RECV) + if(waitfor & REQ_IO_RECV) flags |= CURL_POLL_IN; - if(waitfor & KEEP_SEND) + if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); CURL_TRC_SSH(data, "pollset, flags=%x", flags); @@ -2298,7 +2425,7 @@ if(!sshc || !sshp) return CURLE_FAILED_INIT; - result = myssh_statemach_act(data, sshc, sshp, &block); + result = myssh_statemachine(data, sshc, sshp, &block); *done = (sshc->state == SSH_STOP); myssh_block2waitfor(conn, sshc, block); @@ -2317,7 +2444,7 @@ bool block; timediff_t left_ms = 1000; - result = myssh_statemach_act(data, sshc, sshp, &block); + result = myssh_statemachine(data, sshc, sshp, &block); if(result) break; @@ -2348,7 +2475,7 @@ struct SSHPROTO *sshp = entry; (void)key; (void)klen; - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); curlx_free(sshp); } @@ -2422,13 +2549,9 @@ return CURLE_FAILED_INIT; } - if(conn->bits.ipv6_ip) { - char ipv6[MAX_IPADR_LEN]; - curl_msnprintf(ipv6, sizeof(ipv6), "[%s]", conn->host.name); - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, ipv6); - } - else - rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->host.name); + rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, + (data->state.up.hostname[0] == '[') ? + data->state.up.hostname : conn->host.name); if(rc != SSH_OK) { failf(data, "Could not set remote host"); @@ -2600,7 +2723,7 @@ if(Curl_pgrsDone(data)) return CURLE_ABORTED_BY_CALLBACK; - data->req.keepon = 0; /* clear all bits */ + CURL_REQ_CLEAR_IO(data); return result; } @@ -2637,7 +2760,7 @@ #if 0 /* The following code is misleading, mostly added as wishful thinking - * that libssh at some point will implement non-blocking ssh_scp_write/read. + * that libssh at some point would implement non-blocking ssh_scp_write/read. * Currently rc can only be number of bytes read or SSH_ERROR. */ myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN)); @@ -2671,7 +2794,7 @@ return CURLE_SSH; #if 0 /* The following code is misleading, mostly added as wishful thinking - * that libssh at some point will implement non-blocking ssh_scp_write/read. + * that libssh at some point would implement non-blocking ssh_scp_write/read. * Currently rc can only be SSH_OK or SSH_ERROR. */ myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN)); @@ -2985,7 +3108,7 @@ scp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ }; @@ -3008,7 +3131,7 @@ sftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ ZERO_NULL, /* follow */ };
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c index 63f5735..4e2a722 100644 --- a/Utilities/cmcurl/lib/vssh/libssh2.c +++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -148,7 +148,9 @@ #endif /* CURL_LIBSSH2_DEBUG */ if(num_prompts == 1) { struct connectdata *conn = data->conn; - responses[0].text = curlx_strdup(conn->passwd); + /* this function must allocate memory that can be freed by libssh2, which + uses the LIBSSH2_FREE_FUNC callback */ + responses[0].text = Curl_cstrdup(conn->passwd); responses[0].length = responses[0].text == NULL ? 0 : curlx_uztoui(strlen(conn->passwd)); } @@ -405,7 +407,7 @@ rc = CURLKHSTAT_REJECT; switch(rc) { - default: /* unknown return codes will equal reject */ + default: /* unknown return codes is the same as reject */ case CURLKHSTAT_REJECT: myssh_to(data, sshc, SSH_SESSION_FREE); FALLTHROUGH(); @@ -476,7 +478,7 @@ LIBSSH2_HOSTKEY_HASH_SHA256); if(!fingerprint) { failf(data, - "Denied establishing ssh session: sha256 fingerprint " + "Denied establishing ssh session: SHA256 fingerprint " "not available"); myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; @@ -491,7 +493,7 @@ } if(!fingerprint_b64) { - failf(data, "sha256 fingerprint could not be encoded"); + failf(data, "SHA256 fingerprint could not be encoded"); myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_PEER_FAILED_VERIFICATION; } @@ -509,13 +511,13 @@ b64_pos++; } - /* Before we authenticate we check the hostkey's sha256 fingerprint + /* Before we authenticate we check the hostkey's SHA256 fingerprint * against a known fingerprint, if available. */ if((pub_pos != b64_pos) || strncmp(fingerprint_b64, pubkey_sha256, pub_pos)) { failf(data, - "Denied establishing ssh session: mismatch sha256 fingerprint. " + "Denied establishing ssh session: mismatch SHA256 fingerprint. " "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256); curlx_free(fingerprint_b64); myssh_to(data, sshc, SSH_SESSION_FREE); @@ -545,17 +547,17 @@ infof(data, "SSH MD5 fingerprint: %s", md5buffer); } - /* This does NOT verify the length of 'pubkey_md5' separately, which will - make the comparison below fail unless it is exactly 32 characters */ + /* This does NOT verify the length of 'pubkey_md5' separately, which + makes the comparison below fail unless it is exactly 32 characters */ if(!fingerprint || !curl_strequal(md5buffer, pubkey_md5)) { if(fingerprint) { failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " + "Denied establishing ssh session: mismatch MD5 fingerprint. " "Remote %s is not equal to %s", md5buffer, pubkey_md5); } else { failf(data, - "Denied establishing ssh session: md5 fingerprint " + "Denied establishing ssh session: MD5 fingerprint " "not available"); } myssh_to(data, sshc, SSH_SESSION_FREE); @@ -600,7 +602,7 @@ } /* - * ssh_force_knownhost_key_type() will check the known hosts file and try to + * ssh_force_knownhost_key_type() checks the known hosts file and try to * force a specific public key type from the server if an entry is found. */ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data, @@ -624,7 +626,7 @@ struct connectdata *conn = data->conn; /* lets try to find our host in the known hosts file */ while(!libssh2_knownhost_get(sshc->kh, &store, store)) { - /* For non-standard ports, the name will be enclosed in */ + /* For non-standard ports, the name is enclosed in */ /* square brackets, followed by a colon and the port */ if(store) { if(store->name) { @@ -715,12 +717,12 @@ return result; } -static CURLcode return_quote_error(struct Curl_easy *data, - struct ssh_conn *sshc) +static CURLcode quote_error(struct Curl_easy *data, + struct ssh_conn *sshc) { failf(data, "Suspicious data after the command line"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); return CURLE_QUOTE_ERROR; } @@ -741,8 +743,8 @@ sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command + can, the command is allowed to fail without it causing any + aborts or cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -808,11 +810,11 @@ if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in %s: Bad second parameter", cmd); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES)); myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT); @@ -827,17 +829,17 @@ if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK); return result; } else if(!strncmp(cmd, "mkdir ", 6)) { if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); /* create directory */ myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR); return result; @@ -850,37 +852,37 @@ if(result) { if(result != CURLE_OUT_OF_MEMORY) failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); return result; } if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME); return result; } else if(!strncmp(cmd, "rmdir ", 6)) { if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); /* delete directory */ myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR); return result; } else if(!strncmp(cmd, "rm ", 3)) { if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK); return result; } else if(!strncmp(cmd, "statvfs ", 8)) { if(*cp) - return_quote_error(data, sshc); + return quote_error(data, sshc); myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS); return result; } failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); return CURLE_QUOTE_ERROR; } @@ -894,7 +896,7 @@ /* * NOTE!!! libssh2 requires that the destination path is a full path * that includes the destination file and name OR ends in a "/" - * If this is not done the destination file will be named the + * If this is not done the destination file is named the * same name as the last directory in the path. */ @@ -1054,7 +1056,7 @@ /* upload data */ Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->recv_idx = FIRSTSOCKET; /* since we do not really wait for anything at this point, we want the @@ -1103,7 +1105,7 @@ if(!sshc->rsa) out_of_memory = TRUE; else if(curlx_stat(sshc->rsa, &sbuf)) { - Curl_safefree(sshc->rsa); + curlx_safefree(sshc->rsa); } } curlx_free(home); @@ -1138,8 +1140,8 @@ } if(out_of_memory || !sshc->rsa) { - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->rsa_pub); myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_OUT_OF_MEMORY; } @@ -1157,7 +1159,7 @@ else { myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } - return 0; + return CURLE_OK; } static CURLcode sftp_quote_stat(struct Curl_easy *data, @@ -1169,8 +1171,8 @@ sshc->acceptfail = FALSE; /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any aborts or - cancels etc. It will cause libcurl to act as if the command is + can, the command is allowed to fail without it causing any aborts or + cancels etc. It causes libcurl to act as if the command is successful, whatever the server responds. */ if(cmd[0] == '*') { @@ -1266,8 +1268,8 @@ myssh_to(data, sshc, SSH_SFTP_QUOTE_SETSTAT); return CURLE_OK; fail: - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); return CURLE_QUOTE_ERROR; } @@ -1296,7 +1298,7 @@ data->req.size = -1; data->req.maxdownload = -1; Curl_pgrsSetDownloadSize(data, -1); - attrs.filesize = 0; /* might be uninitialized but will be read below */ + attrs.filesize = 0; /* might be uninitialized but is read below */ } else { curl_off_t size = attrs.filesize; @@ -1358,7 +1360,7 @@ } Curl_xfer_setup_recv(data, FIRSTSOCKET, data->req.size); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; myssh_to(data, sshc, SSH_STOP); @@ -1489,9 +1491,9 @@ * must never change it later. Thus, always specify the correct username * here, even though the libssh2 docs kind of indicate that it should be * possible to get a 'generic' list (not user-specific) of authentication - * methods, presumably with a blank username. That will not work in my + * methods, presumably with a blank username. That does not work in my * experience. - * So always specify it here. + * Therefore always specify it here. */ struct connectdata *conn = data->conn; sshc->authlist = libssh2_userauth_list(sshc->ssh_session, @@ -1535,8 +1537,8 @@ if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); if(rc == 0) { sshc->authed = TRUE; @@ -1801,10 +1803,15 @@ /* * Get the "home" directory */ - int rc = libssh2_sftp_symlink_ex(sshc->sftp_session, - ".", curlx_uztoui(strlen(".")), - sshp->readdir_filename, CURL_PATH_MAX, - LIBSSH2_SFTP_REALPATH); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_symlink_ex(sshc->sftp_session, ".", + curlx_uztoui(strlen(".")), + sshp->readdir_filename, CURL_PATH_MAX, + LIBSSH2_SFTP_REALPATH); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -1835,8 +1842,7 @@ /* This is the last step in the SFTP connect phase. Do note that while we get the homedir here, we get the "workingpath" in the DO action since the - homedir will remain the same between request but the working path will - not. */ + homedir remains the same between request but the working path does not. */ CURL_TRC_SSH(data, "CONNECT phase done"); return CURLE_OK; } @@ -1845,7 +1851,12 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { - CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); if(result) { myssh_to(data, sshc, SSH_STOP); return result; @@ -1881,7 +1892,12 @@ struct SSHPROTO *sshp) { /* Send quote commands */ - CURLcode result = sftp_quote(data, sshc, sshp); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + result = sftp_quote(data, sshc, sshp); if(result) { myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; @@ -1892,8 +1908,8 @@ static CURLcode ssh_state_sftp_next_quote(struct Curl_easy *data, struct ssh_conn *sshc) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); sshc->quote_item = sshc->quote_item->next; @@ -1917,7 +1933,12 @@ struct SSHPROTO *sshp, bool *blockp) { - CURLcode result = sftp_quote_stat(data, sshc, sshp, blockp); + CURLcode result; + + if(!sshp) + return CURLE_FAILED_INIT; + + result = sftp_quote_stat(data, sshc, sshp, blockp); if(result) { myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; @@ -1929,11 +1950,15 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { - int rc = - libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, - curlx_uztoui(strlen(sshc->quote_path2)), - LIBSSH2_SFTP_SETSTAT, - &sshp->quote_attrs); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, + curlx_uztoui(strlen(sshc->quote_path2)), + LIBSSH2_SFTP_SETSTAT, + &sshp->quote_attrs); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -1941,8 +1966,8 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "Attempt to set SFTP stats for \"%s\" failed: %s", sshc->quote_path2, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; @@ -1968,8 +1993,8 @@ failf(data, "symlink \"%s\" to \"%s\" failed: %s", sshc->quote_path1, sshc->quote_path2, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; @@ -1991,7 +2016,7 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "mkdir \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; @@ -2020,8 +2045,8 @@ failf(data, "rename \"%s\" to \"%s\" failed: %s", sshc->quote_path1, sshc->quote_path2, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; @@ -2042,7 +2067,7 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "rmdir \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; @@ -2063,7 +2088,7 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "rm \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; @@ -2087,7 +2112,7 @@ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session); failf(data, "statvfs \"%s\" failed: %s", sshc->quote_path1, sftp_libssh2_strerror(sftperr)); - Curl_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path1); myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; return CURLE_QUOTE_ERROR; @@ -2140,9 +2165,14 @@ struct SSHPROTO *sshp) { /* 'mode' - parameter is preliminary - default to 0644 */ - int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - (long)data->set.new_directory_perms); + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + (long)data->set.new_directory_perms); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -2170,6 +2200,9 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; + Curl_pgrsSetDownloadSize(data, -1); if(data->req.no_body) { myssh_to(data, sshc, SSH_STOP); @@ -2204,13 +2237,16 @@ struct SSHPROTO *sshp) { CURLcode result; - int rc = - libssh2_sftp_symlink_ex(sshc->sftp_session, - curlx_dyn_ptr(&sshp->readdir_link), - (unsigned int) - curlx_dyn_len(&sshp->readdir_link), - sshp->readdir_filename, - CURL_PATH_MAX, LIBSSH2_SFTP_READLINK); + int rc; + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_symlink_ex(sshc->sftp_session, + curlx_dyn_ptr(&sshp->readdir_link), + (unsigned int) + curlx_dyn_len(&sshp->readdir_link), + sshp->readdir_filename, + CURL_PATH_MAX, LIBSSH2_SFTP_READLINK); if(rc == LIBSSH2_ERROR_EAGAIN) return CURLE_AGAIN; @@ -2233,9 +2269,12 @@ struct SSHPROTO *sshp) { curl_off_t bytecount; + libssh2_struct_stat sb; + if(!sshp) + return CURLE_FAILED_INIT; /* - * We must check the remote file; if it is a directory no values will + * We must check the remote file; if it is a directory no values are * be set in sb */ @@ -2244,7 +2283,6 @@ */ /* get a fresh new channel from the ssh layer */ - libssh2_struct_stat sb; memset(&sb, 0, sizeof(libssh2_struct_stat)); sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session, sshp->path, &sb); if(!sshc->ssh_channel) { @@ -2266,7 +2304,7 @@ data->req.maxdownload = (curl_off_t)sb.st_size; Curl_xfer_setup_recv(data, FIRSTSOCKET, bytecount); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->send_idx = 0; myssh_to(data, sshc, SSH_STOP); @@ -2278,6 +2316,8 @@ struct SSHPROTO *sshp) { int rc = 0; + if(!sshp) + return CURLE_FAILED_INIT; if(sshc->sftp_handle) { rc = libssh2_sftp_close(sshc->sftp_handle); if(rc == LIBSSH2_ERROR_EAGAIN) @@ -2291,7 +2331,7 @@ sshc->sftp_handle = NULL; } - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); CURL_TRC_SSH(data, "SFTP DONE done"); @@ -2339,7 +2379,7 @@ sshc->sftp_session = NULL; } - Curl_safefree(sshc->homedir); + curlx_safefree(sshc->homedir); myssh_to(data, sshc, SSH_SESSION_DISCONNECT); return CURLE_OK; @@ -2349,6 +2389,8 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; /* * Work on getting the specified file */ @@ -2377,10 +2419,12 @@ struct ssh_conn *sshc, struct SSHPROTO *sshp) { + if(!sshp) + return CURLE_FAILED_INIT; /* * libssh2 requires that the destination path is a full path that * includes the destination file and name OR ends in a "/" . If this is - * not done the destination file will be named the same name as the last + * not done the destination file is named the same name as the last * directory in the path. */ sshc->ssh_channel = @@ -2412,7 +2456,7 @@ Curl_pgrsSetUploadSize(data, data->state.infilesize); Curl_xfer_setup_send(data, FIRSTSOCKET); - /* not set by Curl_xfer_setup to preserve keepon bits */ + /* not set by Curl_xfer_setup to preserve io_flags */ data->conn->recv_idx = FIRSTSOCKET; myssh_to(data, sshc, SSH_STOP); @@ -2452,7 +2496,7 @@ } } - Curl_safefree(sshc->homedir); + curlx_safefree(sshc->homedir); myssh_to(data, sshc, SSH_SESSION_FREE); return CURLE_OK; @@ -2548,19 +2592,320 @@ DEBUGASSERT(sshc->kh == NULL); DEBUGASSERT(sshc->ssh_agent == NULL); - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - Curl_safefree(sshc->homedir); + curlx_safefree(sshc->rsa_pub); + curlx_safefree(sshc->rsa); + curlx_safefree(sshc->quote_path1); + curlx_safefree(sshc->quote_path2); + curlx_safefree(sshc->homedir); return CURLE_OK; } +static CURLcode ssh_state_sftp_getinfo(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->set.get_filetime) + myssh_to(data, sshc, SSH_SFTP_FILETIME); + else + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_filetime(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + LIBSSH2_SFTP_ATTRIBUTES attrs; + int rc; + + if(!sshp) + return CURLE_FAILED_INIT; + + rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, + curlx_uztoui(strlen(sshp->path)), + LIBSSH2_SFTP_STAT, &attrs); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + + if(rc == 0) + data->info.filetime = (time_t)attrs.mtime; + + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_trans_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + else if(sshp) { + size_t plen = strlen(sshp->path); + if(plen && (sshp->path[plen - 1] == '/')) + myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); + else + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); + } + else + return CURLE_FAILED_INIT; + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_upload_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_upload_init(data, sshc, sshp, block); + if(result) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + } + return result; +} + +static CURLcode ssh_state_sftp_create_dirs_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return CURLE_FAILED_INIT; + if(strlen(sshp->path) > 1) { + sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); + } + else + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_create_dirs(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(!sshp) + return CURLE_FAILED_INIT; + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { + *sshc->slash_pos = 0; + infof(data, "Creating directory '%s'", sshp->path); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); + return CURLE_OK; + } + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_readdir(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_readdir(data, sshc, sshp, block); + if(result) + myssh_to(data, sshc, SSH_SFTP_CLOSE); + return result; +} + +static CURLcode ssh_state_sftp_readdir_bottom(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + + result = curlx_dyn_addn(&sshp->readdir, "\n", 1); + if(!result) + result = Curl_client_write(data, CLIENTWRITE_BODY, + curlx_dyn_ptr(&sshp->readdir), + curlx_dyn_len(&sshp->readdir)); + if(result) { + curlx_dyn_free(&sshp->readdir); + myssh_to(data, sshc, SSH_STOP); + } + else { + curlx_dyn_reset(&sshp->readdir); + myssh_to(data, sshc, SSH_SFTP_READDIR); + } + return result; +} + +static CURLcode ssh_state_sftp_readdir_done(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(libssh2_sftp_closedir(sshc->sftp_handle) == + LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + + sshc->sftp_handle = NULL; + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + myssh_to(data, sshc, SSH_STOP); + return CURLE_OK; +} + +static CURLcode ssh_state_sftp_download_stat(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp, + bool *block) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = sftp_download_stat(data, sshc, sshp, block); + if(result) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + } + return result; +} + +static CURLcode ssh_state_scp_trans_init(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result; + if(!sshp) + return CURLE_FAILED_INIT; + result = Curl_getworkingpath(data, sshc->homedir, + &sshp->path); + if(result) { + myssh_to(data, sshc, SSH_STOP); + return result; + } + + if(data->state.upload) { + if(data->state.infilesize < 0) { + failf(data, "SCP requires a known file size for upload"); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_UPLOAD_FAILED; + } + myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); + } + else + myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_done(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->state.upload) + myssh_to(data, sshc, SSH_SCP_SEND_EOF); + else + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_send_eof(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_send_eof(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, + "Failed to send libssh2 channel EOF: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_WAIT_EOF); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_wait_eof(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_wait_eof(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to get channel EOF: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_wait_close(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = + libssh2_channel_wait_closed(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Channel failed to close: %d %s", + rc, err_msg); + } + } + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + return CURLE_OK; +} + +static CURLcode ssh_state_scp_channel_free( + struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->ssh_channel) { + int rc = libssh2_channel_free(sshc->ssh_channel); + if(rc == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + if(rc < 0) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, + "Failed to free libssh2 scp subsystem: %d %s", + rc, err_msg); + } + sshc->ssh_channel = NULL; + } + CURL_TRC_SSH(data, "SCP DONE phase complete"); + myssh_to(data, sshc, SSH_STOP); + return CURLE_OK; +} + +static CURLcode ssh_state_session_free(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + CURLcode result = sshc_cleanup(sshc, data, FALSE); + struct connectdata *conn = data->conn; + if(result) + return result; + memset(sshc, 0, sizeof(struct ssh_conn)); + connclose(conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ + myssh_to(data, sshc, SSH_STOP); + return CURLE_OK; +} + /* * ssh_statemachine() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points - * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN + * to is set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN * meaning it wants to be called again when the socket is ready */ static CURLcode ssh_statemachine(struct Curl_easy *data, @@ -2569,7 +2914,6 @@ bool *block) { CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; *block = 0; /* we are not blocking by default */ do { @@ -2647,13 +2991,11 @@ break; case SSH_SFTP_REALPATH: - result = sshp ? ssh_state_sftp_realpath(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_realpath(data, sshc, sshp); break; case SSH_SFTP_QUOTE_INIT: - result = sshp ? ssh_state_sftp_quote_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_quote_init(data, sshc, sshp); break; case SSH_SFTP_POSTQUOTE_INIT: @@ -2661,8 +3003,7 @@ break; case SSH_SFTP_QUOTE: - result = sshp ? ssh_state_sftp_quote(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_quote(data, sshc, sshp); break; case SSH_SFTP_NEXT_QUOTE: @@ -2670,13 +3011,11 @@ break; case SSH_SFTP_QUOTE_STAT: - result = sshp ? ssh_state_sftp_quote_stat(data, sshc, sshp, block) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_quote_stat(data, sshc, sshp, block); break; case SSH_SFTP_QUOTE_SETSTAT: - result = sshp ? ssh_state_sftp_quote_setstat(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_quote_setstat(data, sshc, sshp); break; case SSH_SFTP_QUOTE_SYMLINK: @@ -2704,160 +3043,63 @@ break; case SSH_SFTP_GETINFO: - if(data->set.get_filetime) { - myssh_to(data, sshc, SSH_SFTP_FILETIME); - } - else { - myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); - } + result = ssh_state_sftp_getinfo(data, sshc); break; - case SSH_SFTP_FILETIME: { - LIBSSH2_SFTP_ATTRIBUTES attrs; - int rc; - - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path, - curlx_uztoui(strlen(sshp->path)), - LIBSSH2_SFTP_STAT, &attrs); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc == 0) { - data->info.filetime = (time_t)attrs.mtime; - } - - myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); + case SSH_SFTP_FILETIME: + result = ssh_state_sftp_filetime(data, sshc, sshp); break; - } case SSH_SFTP_TRANS_INIT: - if(data->state.upload) - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); - else if(sshp) { - if(sshp->path[strlen(sshp->path) - 1] == '/') - myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); - else - myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); - } - else - result = CURLE_FAILED_INIT; + result = ssh_state_sftp_trans_init(data, sshc, sshp); break; case SSH_SFTP_UPLOAD_INIT: - result = sshp ? sftp_upload_init(data, sshc, sshp, block) : - CURLE_FAILED_INIT; - if(result) { - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - } + result = ssh_state_sftp_upload_init(data, sshc, sshp, block); break; case SSH_SFTP_CREATE_DIRS_INIT: - if(!sshp) - result = CURLE_FAILED_INIT; - else if(strlen(sshp->path) > 1) { - sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); - } - else { - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); - } + result = ssh_state_sftp_create_dirs_init(data, sshc, sshp); break; case SSH_SFTP_CREATE_DIRS: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - sshc->slash_pos = strchr(sshc->slash_pos, '/'); - if(sshc->slash_pos) { - *sshc->slash_pos = 0; - - infof(data, "Creating directory '%s'", sshp->path); - myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); - break; - } - myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); + result = ssh_state_sftp_create_dirs(data, sshc, sshp); break; case SSH_SFTP_CREATE_DIRS_MKDIR: - result = sshp ? ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp); break; case SSH_SFTP_READDIR_INIT: - result = sshp ? ssh_state_sftp_readdir_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_readdir_init(data, sshc, sshp); break; case SSH_SFTP_READDIR: - result = sshp ? sftp_readdir(data, sshc, sshp, block) : - CURLE_FAILED_INIT; - if(result) { - myssh_to(data, sshc, SSH_SFTP_CLOSE); - } + result = ssh_state_sftp_readdir(data, sshc, sshp, block); break; case SSH_SFTP_READDIR_LINK: - result = sshp ? ssh_state_sftp_readdir_link(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_readdir_link(data, sshc, sshp); break; case SSH_SFTP_READDIR_BOTTOM: - if(!sshp) { - result = CURLE_FAILED_INIT; - break; - } - result = curlx_dyn_addn(&sshp->readdir, "\n", 1); - if(!result) - result = Curl_client_write(data, CLIENTWRITE_BODY, - curlx_dyn_ptr(&sshp->readdir), - curlx_dyn_len(&sshp->readdir)); - - if(result) { - curlx_dyn_free(&sshp->readdir); - myssh_to(data, sshc, SSH_STOP); - } - else { - curlx_dyn_reset(&sshp->readdir); - myssh_to(data, sshc, SSH_SFTP_READDIR); - } + result = ssh_state_sftp_readdir_bottom(data, sshc, sshp); break; case SSH_SFTP_READDIR_DONE: - if(libssh2_sftp_closedir(sshc->sftp_handle) == LIBSSH2_ERROR_EAGAIN) - result = CURLE_AGAIN; - else { - sshc->sftp_handle = NULL; - - /* no data to transfer */ - Curl_xfer_setup_nop(data); - myssh_to(data, sshc, SSH_STOP); - } + result = ssh_state_sftp_readdir_done(data, sshc); break; case SSH_SFTP_DOWNLOAD_INIT: - result = sshp ? ssh_state_sftp_download_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_download_init(data, sshc, sshp); break; case SSH_SFTP_DOWNLOAD_STAT: - result = sshp ? sftp_download_stat(data, sshc, sshp, block) : - CURLE_FAILED_INIT; - if(result) { - myssh_to(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - } + result = ssh_state_sftp_download_stat(data, sshc, sshp, block); break; case SSH_SFTP_CLOSE: - result = sshp ? ssh_state_sftp_close(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_sftp_close(data, sshc, sshp); break; case SSH_SFTP_SHUTDOWN: @@ -2865,114 +3107,35 @@ break; case SSH_SCP_TRANS_INIT: - result = sshp ? Curl_getworkingpath(data, sshc->homedir, &sshp->path) : - CURLE_FAILED_INIT; - if(result) { - myssh_to(data, sshc, SSH_STOP); - break; - } - - if(data->state.upload) { - if(data->state.infilesize < 0) { - failf(data, "SCP requires a known file size for upload"); - result = CURLE_UPLOAD_FAILED; - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); - break; - } - myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); - } - else { - myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); - } + result = ssh_state_scp_trans_init(data, sshc, sshp); break; case SSH_SCP_UPLOAD_INIT: - result = sshp ? ssh_state_scp_upload_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_scp_upload_init(data, sshc, sshp); break; case SSH_SCP_DOWNLOAD_INIT: - result = sshp ? ssh_state_scp_download_init(data, sshc, sshp) : - CURLE_FAILED_INIT; + result = ssh_state_scp_download_init(data, sshc, sshp); break; case SSH_SCP_DONE: - if(data->state.upload) - myssh_to(data, sshc, SSH_SCP_SEND_EOF); - else - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + result = ssh_state_scp_done(data, sshc); break; case SSH_SCP_SEND_EOF: - if(sshc->ssh_channel) { - int rc = libssh2_channel_send_eof(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to send libssh2 channel EOF: %d %s", - rc, err_msg); - } - } - myssh_to(data, sshc, SSH_SCP_WAIT_EOF); + result = ssh_state_scp_send_eof(data, sshc); break; case SSH_SCP_WAIT_EOF: - if(sshc->ssh_channel) { - int rc = libssh2_channel_wait_eof(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to get channel EOF: %d %s", rc, err_msg); - } - } - myssh_to(data, sshc, SSH_SCP_WAIT_CLOSE); + result = ssh_state_scp_wait_eof(data, sshc); break; case SSH_SCP_WAIT_CLOSE: - if(sshc->ssh_channel) { - int rc = libssh2_channel_wait_closed(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Channel failed to close: %d %s", rc, err_msg); - } - } - myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); + result = ssh_state_scp_wait_close(data, sshc); break; case SSH_SCP_CHANNEL_FREE: - if(sshc->ssh_channel) { - int rc = libssh2_channel_free(sshc->ssh_channel); - if(rc == LIBSSH2_ERROR_EAGAIN) { - result = CURLE_AGAIN; - break; - } - if(rc < 0) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 scp subsystem: %d %s", - rc, err_msg); - } - sshc->ssh_channel = NULL; - } - CURL_TRC_SSH(data, "SCP DONE phase complete"); - myssh_to(data, sshc, SSH_STOP); + result = ssh_state_scp_channel_free(data, sshc); break; case SSH_SESSION_DISCONNECT: @@ -2980,14 +3143,7 @@ break; case SSH_SESSION_FREE: - result = sshc_cleanup(sshc, data, FALSE); - if(result) - break; - /* the code we are about to return */ - memset(sshc, 0, sizeof(struct ssh_conn)); - connclose(conn, "SSH session free"); - sshc->state = SSH_SESSION_FREE; /* current */ - myssh_to(data, sshc, SSH_STOP); + result = ssh_state_session_free(data, sshc); break; case SSH_QUIT: @@ -3024,12 +3180,12 @@ if(!sshc || (sock == CURL_SOCKET_BAD)) return CURLE_FAILED_INIT; - waitfor = sshc->waitfor ? sshc->waitfor : data->req.keepon; + waitfor = sshc->waitfor ? sshc->waitfor : data->req.io_flags; if(waitfor) { int flags = 0; - if(waitfor & KEEP_RECV) + if(waitfor & REQ_IO_RECV) flags |= CURL_POLL_IN; - if(waitfor & KEEP_SEND) + if(waitfor & REQ_IO_SEND) flags |= CURL_POLL_OUT; DEBUGASSERT(flags); CURL_TRC_SSH(data, "pollset, flags=%x", flags); @@ -3058,8 +3214,9 @@ dir = libssh2_session_block_directions(sshc->ssh_session); if(dir) { /* translate the libssh2 define bits into our own bit defines */ - sshc->waitfor = ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? KEEP_RECV : 0) | - ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? KEEP_SEND : 0); + sshc->waitfor = + ((dir & LIBSSH2_SESSION_BLOCK_INBOUND) ? REQ_IO_RECV : 0) | + ((dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) ? REQ_IO_SEND : 0); } } if(!dir) @@ -3146,7 +3303,7 @@ struct SSHPROTO *sshp = entry; (void)key; (void)klen; - Curl_safefree(sshp->path); + curlx_safefree(sshp->path); curlx_dyn_free(&sshp->readdir); curlx_dyn_free(&sshp->readdir_link); curlx_free(sshp); @@ -3520,7 +3677,7 @@ if(Curl_pgrsDone(data)) return CURLE_ABORTED_BY_CALLBACK; - data->req.keepon = 0; /* clear all bits */ + CURL_REQ_CLEAR_IO(data); return result; } @@ -3835,7 +3992,7 @@ scp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ssh_attach, /* attach */ ZERO_NULL, /* follow */ }; @@ -3858,7 +4015,7 @@ sftp_disconnect, /* disconnect */ ZERO_NULL, /* write_resp */ ZERO_NULL, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ssh_attach, /* attach */ ZERO_NULL, /* follow */ };
diff --git a/Utilities/cmcurl/lib/vssh/ssh.h b/Utilities/cmcurl/lib/vssh/ssh.h index 1afb1e8..44e72ab 100644 --- a/Utilities/cmcurl/lib/vssh/ssh.h +++ b/Utilities/cmcurl/lib/vssh/ssh.h
@@ -29,9 +29,6 @@ extern const struct Curl_protocol Curl_protocol_sftp; extern const struct Curl_protocol Curl_protocol_scp; -extern const struct Curl_scheme Curl_scheme_sftp; -extern const struct Curl_scheme Curl_scheme_scp; - #ifdef USE_SSH #ifdef USE_LIBSSH2 @@ -162,7 +159,7 @@ int secondCreateDirs; /* counter use by the code to see if the second attempt has been made to change to/create a directory */ - int waitfor; /* KEEP_RECV/KEEP_SEND bits overriding + int waitfor; /* REQ_IO_RECV/REQ_IO_SEND bits overriding pollset given flags */ char *slash_pos; /* used by the SFTP_CREATE_DIRS state */
diff --git a/Utilities/cmcurl/lib/vssh/vssh.c b/Utilities/cmcurl/lib/vssh/vssh.c index d499a63..0ba4a9e 100644 --- a/Utilities/cmcurl/lib/vssh/vssh.c +++ b/Utilities/cmcurl/lib/vssh/vssh.c
@@ -331,34 +331,3 @@ } #endif /* USE_SSH */ - -/* - * SFTP protocol handler. - */ -const struct Curl_scheme Curl_scheme_sftp = { - "SFTP", /* scheme */ -#ifndef USE_SSH - NULL, -#else - &Curl_protocol_sftp, -#endif - CURLPROTO_SFTP, /* protocol */ - CURLPROTO_SFTP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE, - PORT_SSH /* defport */ -}; - -const struct Curl_scheme Curl_scheme_scp = { - "SCP", /* scheme */ -#ifndef USE_SSH - NULL, -#else - &Curl_protocol_scp, -#endif - CURLPROTO_SCP, /* protocol */ - CURLPROTO_SCP, /* family */ - PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | /* flags */ - PROTOPT_NOURLQUERY | PROTOPT_CONN_REUSE, - PORT_SSH, /* defport */ -};
diff --git a/Utilities/cmcurl/lib/vtls/apple.c b/Utilities/cmcurl/lib/vtls/apple.c index a6a5ba0..e28a40c 100644 --- a/Utilities/cmcurl/lib/vtls/apple.c +++ b/Utilities/cmcurl/lib/vtls/apple.c
@@ -130,7 +130,7 @@ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); if(!ssl_config->no_revoke) { if(__builtin_available(macOS 10.9, iOS 7, tvOS 9, watchOS 2, *)) { - /* Even without this set, validation will seemingly-unavoidably fail + /* Even without this set, validation seemingly-unavoidably fails * for certificates that trustd already knows to be revoked. * This policy further allows trustd to consult CRLs and OCSP data * to determine revocation status (which it may then cache). */ @@ -142,7 +142,7 @@ * of a cert being NOT REVOKED. Which not in general available for * certificates on the Internet. * It seems that applications using this policy are expected to PIN - * their certificate public keys or verification will fail. + * their certificate public keys or verification fails. * This does not seem to be what we want here. */ if(!ssl_config->revoke_best_effort) { revocation_flags |= kSecRevocationRequirePositiveResponse;
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c index a0a465c..9b6a4fa 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.c +++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -127,7 +127,7 @@ } result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); - CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zd", blen, result, nread); + CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zu", blen, result, nread); backend->gtls.io_result = result; if(result) { /* !checksrc! disable ERRNOVAR 1 */ @@ -203,10 +203,15 @@ f = curlx_fopen(file, "rb"); if(!f) return loaded_file; - if(fseek(f, 0, SEEK_END) != 0 || - (filelen = ftell(f)) < 0 || - fseek(f, 0, SEEK_SET) != 0 || - !(ptr = curlx_malloc((size_t)filelen))) + if(fseek(f, 0, SEEK_END) != 0) + goto out; + filelen = ftell(f); + if(filelen < 0 || filelen > CURL_MAX_INPUT_LENGTH) + goto out; + if(fseek(f, 0, SEEK_SET) != 0) + goto out; + ptr = curlx_malloc((size_t)filelen); + if(!ptr) goto out; if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) { curlx_free(ptr); @@ -257,6 +262,7 @@ return CURLE_AGAIN; } else if((rc < 0) && !gnutls_error_is_fatal(rc)) { +#ifdef CURLVERBOSE const char *strerr = NULL; if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { @@ -268,6 +274,7 @@ strerr = gnutls_strerror(rc); infof(data, "gnutls_handshake() warning: %s", strerr); +#endif return CURLE_AGAIN; } else if((rc < 0) && backend->gtls.io_result) { @@ -304,7 +311,7 @@ #define GNUTLS_CIPHERS "NORMAL:%PROFILE_MEDIUM:-ARCFOUR-128:" \ "-CTYPE-ALL:+CTYPE-X509" -/* If GnuTLS was compiled without support for SRP it will error out if SRP is +/* If GnuTLS was compiled without support for SRP it errors out if SRP is requested in the priority string, so treat it specially */ #define GNUTLS_SRP "+SRP" @@ -710,7 +717,7 @@ /* get the session ID data size */ gnutls_session_get_data(session, NULL, &sdata_len); - if(!sdata_len) /* gnutls does this for some version combinations */ + if(!sdata_len) /* GnuTLS does this for some version combinations */ return CURLE_OK; sdata = curlx_malloc(sdata_len); /* get a buffer for it */ @@ -786,7 +793,7 @@ if(when) { /* after message has been processed */ struct Curl_easy *data = CF_DATA_CURRENT(cf); if(data) { - CURL_TRC_CF(data, cf, "handshake: %s message type %d", + CURL_TRC_CF(data, cf, "handshake: %s message type %u", incoming ? "incoming" : "outgoing", htype); switch(htype) { case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: { @@ -818,7 +825,7 @@ #ifdef USE_GNUTLS_SRP if(conn_config->username) { /* Only add SRP to the cipher list if SRP is requested. Otherwise - * GnuTLS will disable TLS 1.3 support. */ + * GnuTLS disables TLS 1.3 support. */ result = curlx_dyn_add(&buf, priority); if(!result) result = curlx_dyn_add(&buf, ":" GNUTLS_SRP); @@ -1116,7 +1123,7 @@ Curl_alpn_copy(&alpns, alpns_requested); /* This might be a reconnect, so we check for a session ID in the cache - to speed up things. We need to do this before constructing the gnutls + to speed up things. We need to do this before constructing the GnuTLS session since we need to set flags depending on the kind of reuse. */ if(conn_config->cache_session && !conn_config->verifystatus) { result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs); @@ -1138,7 +1145,8 @@ else { infof(data, "SSL reusing session with ALPN '%s'", scs->alpn ? scs->alpn : "-"); - if(ssl_config->earlydata && scs->alpn && !cf->conn->connect_only) { + if(ssl_config->earlydata && scs->alpn && + !cf->conn->bits.connect_only) { bool do_early_data = FALSE; if(sess_reuse_cb) { result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data); @@ -1178,7 +1186,7 @@ #endif /* convert the ALPN string from our arguments to a list of strings that - * gnutls wants and will convert internally back to this string for sending + * GnuTLS wants and does convert internally back to this string for sending * to the server. nice. */ if(!gtls_alpns_count && alpns.count) { size_t i; @@ -1306,7 +1314,7 @@ if(key) gnutls_pubkey_deinit(key); - Curl_safefree(buff1); + curlx_safefree(buff1); return result; } @@ -1579,7 +1587,7 @@ long * const certverifyresult = &ssl_config->certverifyresult; (void)cf; - /* This function will return the peer's raw certificate (chain) as sent by + /* This function returns the peer's raw certificate (chain) as sent by the peer. These certificates are in raw format (DER encoded for X.509). In case of a X.509 then a certificate list may be present. The first certificate in the list is the peer's certificate, following the @@ -1611,7 +1619,7 @@ if(data->set.ssl.certinfo && chain.certs) { if(chain.num_certs > MAX_ALLOWED_CERT_AMOUNT) { - failf(data, "%u certificates is more than allowed (%u)", + failf(data, "%u certificates is more than allowed (%d)", chain.num_certs, MAX_ALLOWED_CERT_AMOUNT); result = CURLE_SSL_CONNECT_ERROR; goto out; @@ -1637,7 +1645,7 @@ if(config->verifypeer) { bool verified = FALSE; unsigned int verify_status = 0; - /* This function will try to verify the peer's certificate and return + /* This function tries to verify the peer's certificate and return its status (trusted, invalid etc.). The value of status should be one or more of the gnutls_certificate_status_t enumerated elements bitwise or'd. To avoid denial of service attacks some default @@ -1693,7 +1701,7 @@ /* initialize an X.509 certificate structure. */ if(gnutls_x509_crt_init(&x509_cert)) { - failf(data, "failed to init gnutls x509_crt"); + failf(data, "failed to init GnuTLS x509_crt"); *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; result = CURLE_SSL_CONNECT_ERROR; goto out; @@ -1777,7 +1785,7 @@ if(config->issuercert) { gnutls_datum_t issuerp; if(gnutls_x509_crt_init(&x509_issuer)) { - failf(data, "failed to init gnutls x509_crt for issuer"); + failf(data, "failed to init GnuTLS x509_crt for issuer"); result = CURLE_SSL_ISSUER_ERROR; goto out; } @@ -1796,7 +1804,7 @@ config->issuercert ? config->issuercert : "none"); } - /* This function will check if the given certificate's subject matches the + /* This function checks if the given certificate's subject matches the given hostname. This is a basic implementation of the matching described in RFC2818 (HTTPS), which takes into account wildcards, and the subject alternative name PKIX extension. Returns non zero on success, and zero on @@ -1890,7 +1898,7 @@ goto out; } else if(!n) { - /* gnutls is buggy, it *SHOULD* return the amount of bytes it took in. + /* GnuTLS is buggy, it *SHOULD* return the amount of bytes it took in. * Instead it returns 0 if everything was written. */ n = (ssize_t)blen; }
diff --git a/Utilities/cmcurl/lib/vtls/gtls.h b/Utilities/cmcurl/lib/vtls/gtls.h index a5e55cd..49106dd 100644 --- a/Utilities/cmcurl/lib/vtls/gtls.h +++ b/Utilities/cmcurl/lib/vtls/gtls.h
@@ -89,7 +89,7 @@ struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - const struct alpn_spec *alpns, + const struct alpn_spec *alpns_requested, Curl_gtls_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data,
diff --git a/Utilities/cmcurl/lib/vtls/keylog.c b/Utilities/cmcurl/lib/vtls/keylog.c index 0206e32..4ae2387 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.c +++ b/Utilities/cmcurl/lib/vtls/keylog.c
@@ -22,27 +22,23 @@ * ***************************************************************************/ #include "curl_setup.h" +#include "vtls/keylog.h" -#if defined(USE_OPENSSL) || \ - defined(USE_GNUTLS) || \ - defined(USE_WOLFSSL) || \ - (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \ - defined(USE_QUICHE) || \ +#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ defined(USE_RUSTLS) -#include "vtls/keylog.h" #include "escape.h" #include "curlx/fopen.h" /* The fp for the open SSLKEYLOGFILE, or NULL if not open */ static FILE *keylog_file_fp; +/* Used for verbose logging */ +static char *keylog_file_name; void Curl_tls_keylog_open(void) { - char *keylog_file_name; - if(!keylog_file_fp) { - keylog_file_name = curl_getenv("SSLKEYLOGFILE"); + keylog_file_name = getenv("SSLKEYLOGFILE"); if(keylog_file_name) { keylog_file_fp = curlx_fopen(keylog_file_name, FOPEN_APPENDTEXT); if(keylog_file_fp) { @@ -56,7 +52,6 @@ keylog_file_fp = NULL; } } - Curl_safefree(keylog_file_name); } } } @@ -74,6 +69,11 @@ return keylog_file_fp != NULL; } +const char *Curl_tls_keylog_file_name(void) +{ + return keylog_file_name; +} + bool Curl_tls_keylog_write_line(const char *line) { /* The current maximum valid keylog line length LF and NUL is 195. */ @@ -145,4 +145,16 @@ return TRUE; } -#endif /* TLS or QUIC backend */ +#else /* TLS backend */ + +bool Curl_tls_keylog_enabled(void) +{ + return FALSE; +} + +const char *Curl_tls_keylog_file_name(void) +{ + return NULL; +} + +#endif /* TLS backend */
diff --git a/Utilities/cmcurl/lib/vtls/keylog.h b/Utilities/cmcurl/lib/vtls/keylog.h index 7e741b9..68ded47 100644 --- a/Utilities/cmcurl/lib/vtls/keylog.h +++ b/Utilities/cmcurl/lib/vtls/keylog.h
@@ -53,12 +53,17 @@ bool Curl_tls_keylog_enabled(void); /* + * Returns a pointer to the filename keys are being written to, if enabled. + */ +const char *Curl_tls_keylog_file_name(void); + +/* * Appends a key log file entry. * Returns true iff the key log file is open and a valid entry was provided. */ bool Curl_tls_keylog_write(const char *label, - const unsigned char client_random[32], - const unsigned char *secret, size_t secretlen); + const unsigned char client_random[CLIENT_RANDOM_SIZE], + const unsigned char *secret, size_t secretlen); /* * Appends a line to the key log file, ensure it is terminated by an LF.
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c index 2bac406..e282266 100644 --- a/Utilities/cmcurl/lib/vtls/mbedtls.c +++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -265,9 +265,11 @@ { uint16_t id = Curl_cipher_suite_walk_str(str, end); size_t len = *end - *str; + static const char ecjpake_suite[] = "TLS_ECJPAKE_WITH_AES_128_CCM_8"; if(!id) { - if(curl_strnequal("TLS_ECJPAKE_WITH_AES_128_CCM_8", *str, len)) + if((len == sizeof(ecjpake_suite) - 1) && + curl_strnequal(ecjpake_suite, *str, len)) id = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8; } return id; @@ -423,7 +425,7 @@ cert_count++; if(cert_count > MAX_ALLOWED_CERT_AMOUNT) { - infof(data, "Certificates is more than allowed (%u), skipping certinfo", + infof(data, "More certificates than allowed (%d), skipping certinfo", MAX_ALLOWED_CERT_AMOUNT); return; } @@ -517,8 +519,8 @@ curlx_free(newblob); } #else - /* DER encoded certs do not need to be null terminated because it is a - binary format. So if we are not compiling with PEM_PARSE we can avoid + /* DER encoded certs do not need to be null-terminated because it is a + binary format. Thus, if we are not compiling with PEM_PARSE we can avoid the extra memory copies altogether. */ ret = mbedtls_x509_crt_parse_der(&backend->cacert, ca_info_blob->data, ca_info_blob->len); @@ -630,8 +632,8 @@ curlx_free(newblob); } #else - /* DER encoded certs do not need to be null terminated because it is a - binary format. So if we are not compiling with PEM_PARSE we can avoid + /* DER encoded certs do not need to be null-terminated because it is a + binary format. Thus, if we are not compiling with PEM_PARSE we can avoid the extra memory copies altogether. */ ret = mbedtls_x509_crt_parse_der(&backend->clicert, ssl_cert_blob->data, ssl_cert_blob->len); @@ -932,8 +934,8 @@ if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ? connssl->peer.sni : connssl->peer.hostname)) { /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and - the name to set in the SNI extension. So even if curl connects to a - host specified as an IP address, this function must be used. */ + the name to set in the SNI extension. Thus even if curl connects to + a host specified as an IP address, this function must be used. */ failf(data, "Failed to set SNI"); return CURLE_SSL_CONNECT_ERROR; } @@ -1210,7 +1212,7 @@ connssl->io_need = CURL_SSL_IO_NEED_NONE; /* mbedTLS is picky when a mbedtls_ssl_write() was previously blocked. * It requires to be called with the same amount of bytes again, or it - * will lose bytes, e.g. reporting all was sent but they were not. + * loses bytes, e.g. reporting all was sent but they were not. * Remember the blocked length and use that when set. */ if(backend->send_blocked) { DEBUGASSERT(backend->send_blocked_len <= len); @@ -1353,15 +1355,15 @@ (void)data; DEBUGASSERT(backend); - if(backend->initialized) { - mbedtls_pk_free(&backend->pk); - mbedtls_x509_crt_free(&backend->clicert); - mbedtls_x509_crt_free(&backend->cacert); + mbedtls_pk_free(&backend->pk); + mbedtls_x509_crt_free(&backend->clicert); + mbedtls_x509_crt_free(&backend->cacert); #ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl_free(&backend->crl); + mbedtls_x509_crl_free(&backend->crl); #endif - Curl_safefree(backend->ciphersuites); - mbedtls_ssl_config_free(&backend->config); + curlx_safefree(backend->ciphersuites); + mbedtls_ssl_config_free(&backend->config); + if(backend->initialized) { mbedtls_ssl_free(&backend->ssl); backend->initialized = FALSE; } @@ -1511,11 +1513,9 @@ ret = mbedtls_ctr_drbg_seed(&rng.drbg, mbedtls_entropy_func, &rng.entropy, NULL, 0); - if(ret) { - failf(NULL, "failed: mbedtls_ctr_drbg_seed returned -0x%x", - (unsigned int)-ret); + if(ret) + /* mbedtls_ctr_drbg_seed returned error */ return 0; - } /* To prevent an adversary from reading your random data, you can enable prediction resistance.
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 50bf1e0..4629ca4 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -27,40 +27,7 @@ */ #include "curl_setup.h" -#if defined(USE_QUICHE) || defined(USE_OPENSSL) - -#ifdef USE_WIN32_CRYPTO -#include <wincrypt.h> -/* If <wincrypt.h> is included directly, or indirectly via <schannel.h>, - * <winldap.h>, <iphlpapi.h>, or something else, <wincrypt.h> does this: - * #define X509_NAME ((LPCSTR)7) - * - * And in BoringSSL/AWC-LC's <openssl/base.h> there is: - * typedef struct X509_name_st X509_NAME; - * etc. - * - * The redefined symbols break these OpenSSL headers when included after - * <wincrypt.h>. - * The workaround is to undefine those defines here (and only here). - * - * For unity builds it may need to be repeated elsewhere too, e.g. in ldap.c, - * to apply to other sources using OpenSSL includes. Each compilation unit - * needs undefine them between the first <wincrypt.h> include and the first - * OpenSSL include. - * - * OpenSSL does this in <openssl/ssl.h> and <openssl/x509v3.h>, but it - * also does the #undef by including <openssl/ossl_typ.h>. <3.1.0 only does - * it on the first include. - * - * LibreSSL automatically undefines these symbols before using them. - */ -#undef X509_NAME -#undef X509_EXTENSIONS -#undef PKCS7_ISSUER_AND_SERIAL -#undef PKCS7_SIGNER_INFO -#undef OCSP_REQUEST -#undef OCSP_RESPONSE -#endif +#ifdef USE_OPENSSL #include "urldata.h" #include "curl_trc.h" @@ -70,6 +37,7 @@ #include "curlx/inet_pton.h" #include "vtls/openssl.h" #include "connect.h" +#include "cf-dns.h" #include "progress.h" #include "vtls/vtls.h" #include "vtls/vtls_int.h" @@ -84,6 +52,9 @@ #include "curlx/strcopy.h" #include "curlx/strdup.h" #include "vtls/apple.h" +#ifdef USE_ECH +#include "curlx/base64.h" +#endif #include <openssl/rand.h> #include <openssl/x509v3.h> @@ -308,7 +279,7 @@ #else RSA_get0_key(rsa, &n, &e, NULL); #endif /* HAVE_EVP_PKEY_GET_PARAMS */ - BIO_printf(mem, "%d", n ? BN_num_bits(n) : 0); + BIO_printf(mem, "%d", (int)(n ? BN_num_bits(n) : 0)); result = push_certinfo(data, mem, "RSA Public Key", i); if(!result) { result = print_pubkey_BN(rsa, n, i); @@ -413,7 +384,7 @@ numcerts = sk_X509_num(sk); if(numcerts > MAX_ALLOWED_CERT_AMOUNT) { - failf(data, "%d certificates is more than allowed (%u)", (int)numcerts, + failf(data, "%d certificates is more than allowed (%d)", (int)numcerts, MAX_ALLOWED_CERT_AMOUNT); return CURLE_SSL_CONNECT_ERROR; } @@ -544,10 +515,6 @@ return result; } -#endif /* USE_QUICHE || USE_OPENSSL */ - -#ifdef USE_OPENSSL - static int ossl_bio_cf_create(BIO *bio) { BIO_set_shutdown(bio, 1); @@ -658,7 +625,7 @@ } /* Before returning server replies to the SSL instance, we need - * to have setup the x509 store or verification will fail. */ + * to have setup the x509 store or verification fails. */ if(!octx->x509_store_setup) { r2 = Curl_ssl_setup_x509_store(cf, data, octx); if(r2) { @@ -1783,7 +1750,7 @@ } OSSL_LIB_CTX_free(data->state.libctx); data->state.libctx = NULL; - Curl_safefree(data->state.propq); + curlx_safefree(data->state.propq); data->state.provider_loaded = FALSE; } @@ -2138,9 +2105,9 @@ case GEN_DNS: /* name/pattern comparison */ /* The OpenSSL man page explicitly says: "In general it cannot be assumed that the data returned by ASN1_STRING_data() is null - terminated or does not contain embedded nulls." But also that - "The actual format of the data will depend on the actual string - type itself: for example for an IA5String the data will be ASCII" + terminated or does not contain embedded nulls.", but also that + "The actual format of the data depends on the actual string + type itself: for example for an IA5String the data is ASCII" It has been however verified that in 0.9.6 and 0.9.7, IA5String is always null-terminated. @@ -2198,7 +2165,7 @@ i = j; } - /* we have the name entry and we will now convert this to a string + /* we have the name entry and we now convert this to a string that we can use for comparison. Doing this we support BMPstring, UTF8, etc. */ @@ -2405,31 +2372,6 @@ static const char *ssl_msg_type(int ssl_ver, int msg) { -#ifdef SSL2_VERSION_MAJOR /* OpenSSL 1.0.2, LibreSSL <=3.9.2 */ - if(ssl_ver == SSL2_VERSION_MAJOR) { - switch(msg) { - case SSL2_MT_ERROR: - return "Error"; - case SSL2_MT_CLIENT_HELLO: - return "Client hello"; - case SSL2_MT_CLIENT_MASTER_KEY: - return "Client key"; - case SSL2_MT_CLIENT_FINISHED: - return "Client finished"; - case SSL2_MT_SERVER_HELLO: - return "Server hello"; - case SSL2_MT_SERVER_VERIFY: - return "Server verify"; - case SSL2_MT_SERVER_FINISHED: - return "Server finished"; - case SSL2_MT_REQUEST_CERTIFICATE: - return "Request CERT"; - case SSL2_MT_CLIENT_CERTIFICATE: - return "Client CERT"; - } - } - else -#endif if(ssl_ver == SSL3_VERSION_MAJOR) { switch(msg) { case SSL3_MT_HELLO_REQUEST: @@ -2528,11 +2470,6 @@ return; switch(ssl_ver) { -#ifdef SSL2_VERSION /* removed in recent versions */ - case SSL2_VERSION: - verstr = "SSLv2"; - break; -#endif #ifdef SSL3_VERSION case SSL3_VERSION: verstr = "SSLv3"; @@ -2583,7 +2520,7 @@ ssl_ver >>= 8; /* check the upper 8 bits only below */ /* SSLv2 does not seem to have TLS record-type headers, so OpenSSL - * always pass-up content-type as 0. But the interesting message-type + * always pass-up content-type as 0, but the interesting message-type * is at 'buf[0]'. */ if(ssl_ver == SSL3_VERSION_MAJOR && content_type) @@ -2678,7 +2615,7 @@ case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */ default: /* SSL_CTX_set_max_proto_version states that: setting the maximum to 0 - will enable protocol versions up to the highest version supported by + enables protocol versions up to the highest version supported by the library */ ossl_ssl_version_max = 0; break; @@ -2850,6 +2787,7 @@ #ifdef USE_WIN32_CRYPTO static CURLcode ossl_win_load_store(struct Curl_easy *data, + struct Curl_cfilter *cf, const char *win_store, X509_STORE *store, bool *padded) @@ -2862,11 +2800,13 @@ hStore = CertOpenSystemStoreA(0, win_store); if(hStore) { PCCERT_CONTEXT pContext = NULL; - /* The array of enhanced key usage OIDs will vary per certificate and + /* The array of enhanced key usage OIDs varies per certificate and is declared outside of the loop so that rather than malloc/free each iteration we can grow it with realloc, when necessary. */ CERT_ENHKEY_USAGE *enhkey_usage = NULL; DWORD enhkey_usage_size = 0; + VERBOSE(size_t total = 0); + VERBOSE(size_t imported = 0); /* This loop makes a best effort to import all valid certificates from the MS root store. If a certificate cannot be imported it is @@ -2883,8 +2823,10 @@ if(!pContext) break; + VERBOSE(++total); + #if defined(DEBUGBUILD) && defined(CURLVERBOSE) - else { + { char cert_name[256]; if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, cert_name, sizeof(cert_name))) @@ -2972,6 +2914,7 @@ such as duplicate certificate, which is allowed by MS but not OpenSSL. */ if(X509_STORE_add_cert(store, x509) == 1) { + VERBOSE(++imported); #ifdef DEBUGBUILD infof(data, "SSL: Imported cert"); #endif @@ -2984,6 +2927,10 @@ CertFreeCertificateContext(pContext); CertCloseStore(hStore, 0); + CURL_TRC_CF(data, cf, + "ossl_win_load_store() found: %zu imported: %zu certs in %s.", + total, imported, win_store); + if(result) return result; } @@ -2999,7 +2946,7 @@ /* Import certificates from the Windows root certificate store if requested. https://stackoverflow.com/questions/9507184/ - https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 + https://github.com/d3x0r/SACK/blob/ff15424d3c581b86d40f818532e5a400c516d39d/src/netlib/ssl_layer.c#L1410 https://datatracker.ietf.org/doc/html/rfc5280 */ const char *win_stores[] = { "ROOT", /* Trusted Root Certification Authorities */ @@ -3011,7 +2958,7 @@ *padded = FALSE; for(i = 0; i < CURL_ARRAYSIZE(win_stores); ++i) { bool store_added = FALSE; - result = ossl_win_load_store(data, win_stores[i], store, &store_added); + result = ossl_win_load_store(data, cf, win_stores[i], store, &store_added); if(result) return result; if(store_added) { @@ -3126,7 +3073,7 @@ #ifdef CURL_CA_FALLBACK if(octx->store_is_empty) { - /* verifying the peer without any CA certificates will not + /* verifying the peer without any CA certificates does not work so use OpenSSL's built-in default as fallback */ X509_STORE_set_default_paths(store); infof(data, " OpenSSL default paths (fallback)"); @@ -3428,7 +3375,7 @@ #ifdef HAVE_OPENSSL_EARLYDATA if(ssl_config->earlydata && scs->alpn && SSL_SESSION_get_max_early_data(ssl_session) && - !cf->conn->connect_only && + !cf->conn->bits.connect_only && (SSL_version(octx->ssl) == TLS1_3_VERSION)) { bool do_early_data = FALSE; if(sess_reuse_cb) { @@ -3477,6 +3424,16 @@ } #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST +bool Curl_ossl_need_httpsrr(struct Curl_easy *data) +{ + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +} + static CURLcode ossl_init_ech(struct ossl_ctx *octx, struct Curl_cfilter *cf, struct Curl_easy *data, @@ -3486,9 +3443,9 @@ size_t ech_config_len = 0; char *outername = data->set.str[STRING_ECH_PUBLIC]; int trying_ech_now = 0; - CURLcode result; + CURLcode result = CURLE_OK; - if(!ECH_ENABLED(data)) + if(!CURLECH_ENABLED(data)) return CURLE_OK; if(data->set.tls_ech & CURLECH_GREASE) { @@ -3542,44 +3499,32 @@ infof(data, "ECH: ECHConfig from command line"); } else { - struct Curl_dns_entry *dns = NULL; + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); - if(peer->hostname) - dns = Curl_dnscache_get(data, peer->hostname, peer->port, - cf->conn->ip_version); - if(!dns) { - infof(data, "ECH: requested but no DNS info available"); - if(data->set.tls_ech & CURLECH_HARD) - return CURLE_SSL_CONNECT_ERROR; - } - else { - struct Curl_https_rrinfo *rinfo = NULL; + if(rinfo && rinfo->echconfiglist) { + const unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; - rinfo = dns->hinfo; - if(rinfo && rinfo->echconfiglist) { - unsigned char *ecl = rinfo->echconfiglist; - size_t elen = rinfo->echconfiglist_len; - - infof(data, "ECH: ECHConfig from DoH HTTPS RR"); - if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { - infof(data, "ECH: SSL_set1_ech_config_list failed"); - if(data->set.tls_ech & CURLECH_HARD) - return CURLE_SSL_CONNECT_ERROR; - } - else { - trying_ech_now = 1; - infof(data, "ECH: imported ECHConfigList of length %zu", elen); - } - } - else { - infof(data, "ECH: requested but no ECHConfig available"); + infof(data, "ECH: ECHConfig from HTTPS RR"); + if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) { + infof(data, "ECH: SSL_set1_ech_config_list failed"); if(data->set.tls_ech & CURLECH_HARD) return CURLE_SSL_CONNECT_ERROR; } - Curl_resolv_unlink(data, &dns); + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %zu", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; } } #ifdef HAVE_BORINGSSL_LIKE + (void)peer; if(trying_ech_now && outername) { infof(data, "ECH: setting public_name not supported with BoringSSL"); return CURLE_SSL_CONNECT_ERROR; @@ -3605,7 +3550,13 @@ return CURLE_OK; } -#endif /* HAVE_SSL_SET1_ECH_CONFIG_LIST */ +#else /* HAVE_SSL_SET1_ECH_CONFIG_LIST */ +bool Curl_ossl_need_httpsrr(struct Curl_easy *data) +{ + (void)data; + return FALSE; +} +#endif /* else HAVE_SSL_SET1_ECH_CONFIG_LIST */ static CURLcode ossl_init_ssl(struct ossl_ctx *octx, struct Curl_cfilter *cf, @@ -3672,7 +3623,7 @@ case CURL_SSLVERSION_TLSv1_1: case CURL_SSLVERSION_TLSv1_2: case CURL_SSLVERSION_TLSv1_3: - /* it will be handled later with the context options */ + /* it is handled later with the context options */ *pmethod = TLS_client_method(); break; case CURL_SSLVERSION_SSLv2: @@ -4077,7 +4028,7 @@ #ifdef HAVE_SSL_SET0_WBIO /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works * without backward compat quirks. Every call takes one reference, so we - * up it and pass. SSL* then owns it and will free. + * up it and pass. SSL* then owns and frees it. * We check on the function in configure, since LibreSSL and friends * each have their own versions to add support for this. */ BIO_up_ref(bio); @@ -4100,8 +4051,8 @@ #ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST /* If we have retry configs, then trace those out */ -static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl, - int reason) +static int ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL *ssl, + int reason) { CURLcode result = CURLE_OK; size_t rcl = 0; @@ -4117,10 +4068,11 @@ size_t out_name_len = 0; int servername_type = 0; #endif + NOVERBOSE((void)reason); /* nothing to trace if not doing ECH */ - if(!ECH_ENABLED(data)) - return; + if(!CURLECH_ENABLED(data)) + return rv; #ifndef HAVE_BORINGSSL_LIKE rv = SSL_ech_get1_retry_config(ssl, &rcs, &rcl); #else @@ -4153,9 +4105,11 @@ else infof(data, "ECH: no retry_configs (rv = %d)", rv); #ifndef HAVE_BORINGSSL_LIKE - OPENSSL_free((void *)rcs); + OPENSSL_free(inner); + OPENSSL_free(rcs); + OPENSSL_free(outer); #endif - return; + return rv; } #endif @@ -4244,8 +4198,12 @@ reason = ERR_GET_REASON(errdetail); if((lib == ERR_LIB_SSL) && - ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || - (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { + ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) +/* Missing from OpenSSL 4+ OPENSSL_NO_DEPRECATED_3_0 builds */ +#ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED + || (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED) +#endif + )) { result = CURLE_PEER_FAILED_VERIFICATION; lerr = SSL_get_verify_result(octx->ssl); @@ -4317,51 +4275,52 @@ Curl_ossl_report_handshake(data, octx); #if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST) && !defined(HAVE_BORINGSSL_LIKE) - if(ECH_ENABLED(data)) { + if(CURLECH_ENABLED(data)) { char *inner = NULL, *outer = NULL; - const char *status = NULL; int rv; + VERBOSE(const char *status); rv = SSL_ech_get1_status(octx->ssl, &inner, &outer); switch(rv) { case SSL_ECH_STATUS_SUCCESS: - status = "succeeded"; + VERBOSE(status = "succeeded"); break; case SSL_ECH_STATUS_GREASE_ECH: - status = "sent GREASE, got retry-configs"; + VERBOSE(status = "sent GREASE, got retry-configs"); break; case SSL_ECH_STATUS_GREASE: - status = "sent GREASE"; + VERBOSE(status = "sent GREASE"); break; case SSL_ECH_STATUS_NOT_TRIED: - status = "not attempted"; + VERBOSE(status = "not attempted"); break; case SSL_ECH_STATUS_NOT_CONFIGURED: - status = "not configured"; + VERBOSE(status = "not configured"); break; case SSL_ECH_STATUS_BACKEND: - status = "backend (unexpected)"; + VERBOSE(status = "backend (unexpected)"); break; case SSL_ECH_STATUS_FAILED: - status = "failed"; + VERBOSE(status = "failed"); break; case SSL_ECH_STATUS_BAD_CALL: - status = "bad call (unexpected)"; + VERBOSE(status = "bad call (unexpected)"); break; case SSL_ECH_STATUS_BAD_NAME: { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); if(!conn_config->verifypeer && !conn_config->verifyhost && inner && !strcmp(inner, connssl->peer.hostname)) { - status = "bad name (tolerated without peer verification)"; + VERBOSE(status = "bad name (tolerated without peer verification)"); rv = SSL_ECH_STATUS_SUCCESS; } - else - status = "bad name (unexpected)"; + else { + VERBOSE(status = "bad name (unexpected)"); + } break; } default: - status = "unexpected status"; + VERBOSE(status = "unexpected status"); infof(data, "ECH: unexpected status %d", rv); } infof(data, "ECH: result: status is %s, inner is %s, outer is %s", @@ -4437,7 +4396,7 @@ /* * These checks are verifying we got back the same values as when we * sized the buffer. it is pretty weak since they should always be the - * same. But it gives us something to test. + * same, but it gives us something to test. */ if((len1 != len2) || !temp || ((temp - buff1) != len1)) break; /* failed */ @@ -4744,16 +4703,26 @@ unsigned char *ocsp_data = NULL; #endif long ocsp_len = 0; + bool ocsp_missing = FALSE; if(conn_config->verifystatus && !octx->reused_session) ocsp_len = (long)SSL_get_tlsext_status_ocsp_resp(octx->ssl, &ocsp_data); /* SSL_get_tlsext_status_ocsp_resp() returns the length of the OCSP response data or -1 if there is no OCSP response data. */ - if(ocsp_len < 0) + if(ocsp_len < 0) { ocsp_len = 0; /* no data available */ + ocsp_missing = TRUE; + } result = Curl_vtls_apple_verify(cf, data, peer, chain.num_certs, ossl_chain_get_der, &chain, ocsp_data, ocsp_len); + if(!result && ocsp_missing && conn_config->verifystatus && + !octx->reused_session) { + /* verified, but OCSP stapling is required and server sent none */ + *pverified = TRUE; + failf(data, "No OCSP response received"); + return CURLE_SSL_INVALIDCERTSTATUS; + } } *pverified = !result; return result; @@ -4992,6 +4961,11 @@ connssl->io_need = CURL_SSL_IO_NEED_NONE; if(ssl_connect_1 == connssl->connecting_state) { + if(Curl_ossl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect"); + return CURLE_OK; + } CURL_TRC_CF(data, cf, "ossl_connect, step1"); result = ossl_connect_step1(cf, data); if(result) @@ -5085,7 +5059,7 @@ memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; if(octx->blocked_ssl_write_len && (octx->blocked_ssl_write_len != memlen)) { /* The previous SSL_write() call was blocked, using that length. - * We need to use that again or OpenSSL will freak out. A shorter + * We need to use that again or OpenSSL freaks out. A shorter * length should not happen and is a bug in libcurl. */ if(octx->blocked_ssl_write_len > memlen) { DEBUGASSERT(0); @@ -5237,7 +5211,7 @@ result = CURLE_RECV_ERROR; } else { - /* We should no longer get here nowadays. But handle + /* We should no longer get here nowadays, but handle * the error in case of some weirdness in the OSSL stack */ int sockerr = SOCKERRNO; if(sockerr) @@ -5270,13 +5244,15 @@ return result; } -static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, +static CURLcode ossl_get_channel_binding(struct Curl_easy *data, + int sockindex, struct dynbuf *binding) { X509 *cert; - int algo_nid; - const EVP_MD *algo_type; - const char *algo_name; + int mdnid; + bool no_digest_acceptable = FALSE; + const EVP_MD *algo_type = NULL; + const char *algo_name = NULL; unsigned int length; unsigned char buf[EVP_MAX_MD_SIZE]; @@ -5308,43 +5284,108 @@ /* No server certificate, do not do channel binding */ return CURLE_OK; - if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &algo_nid, NULL)) { +#ifdef HAVE_OPENSSL3 + { + int pknid, secbits; + uint32_t flags; + EVP_PKEY *pkey = X509_get0_pubkey(cert); + + if(!X509_get_signature_info(cert, &mdnid, &pknid, &secbits, &flags)) { + failf(data, "certificate signature algorithm not recognized"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; + } + + if(mdnid != NID_undef) { + if(mdnid == NID_md5 || mdnid == NID_sha1) { + algo_type = EVP_sha256(); + } + else + algo_type = EVP_get_digestbynid(mdnid); + } + else if(pkey && !EVP_PKEY_is_a(pkey, OBJ_nid2sn(pknid))) { + /* The cert's pkey is different from the algorithm used to sign + * the certificate. Since the reported `mdnid` is undefined, there + * is no digest algorithm available here. This happens in PQC + * and is accepted, resulting in no addition to the binding. */ + no_digest_acceptable = TRUE; + } + else if(pkey) { + /* cert's pkey type is the same as the cert signer (or same family). + * Ask for the mandatory/advisory digest algorithm for the pkey. + */ + char mdname[128] = ""; + int rc = EVP_PKEY_get_default_digest_name(pkey, mdname, sizeof(mdname)); + bool md_is_undef = !strcmp(mdname, "UNDEF"); + + if(rc == 2 && md_is_undef) { + /* OpenSSL declares "undef" the *mandatory* digest for this key. + * This is some PQC shit, accept it, no addition to binding. */ + no_digest_acceptable = TRUE; + } + else if(rc > 0 && mdname[0] != '\0' && !md_is_undef) { + infof(data, "Digest algorithm : %s%s (derived from public key)" + ", but unavailable", + mdname, rc == 2 ? " [mandatory]" : " [advisory]"); + } + } + } +#else /* HAVE_OPENSSL3 */ + + if(!OBJ_find_sigid_algs(X509_get_signature_nid(cert), &mdnid, NULL)) { failf(data, "Unable to find digest NID for certificate signature algorithm"); result = CURLE_SSL_INVALIDCERTSTATUS; - goto error; + goto out; } /* https://datatracker.ietf.org/doc/html/rfc5929#section-4.1 */ - if(algo_nid == NID_md5 || algo_nid == NID_sha1) { + if(mdnid == NID_md5 || mdnid == NID_sha1) { algo_type = EVP_sha256(); } else { - algo_type = EVP_get_digestbynid(algo_nid); + algo_type = EVP_get_digestbynid(mdnid); if(!algo_type) { - algo_name = OBJ_nid2sn(algo_nid); + algo_name = OBJ_nid2sn(mdnid); failf(data, "Could not find digest algorithm %s (NID %d)", - algo_name ? algo_name : "(null)", algo_nid); + algo_name ? algo_name : "(null)", mdnid); result = CURLE_SSL_INVALIDCERTSTATUS; - goto error; + goto out; } } - if(!X509_digest(cert, algo_type, buf, &length)) { - failf(data, "X509_digest() failed"); +#endif /* HAVE_OPENSSL3, else */ + + if(!algo_type) { + if(no_digest_acceptable) { + infof(data, "certificate exposes no signing digest algorithm, " + "nothing to add to channel binding"); + result = CURLE_OK; + goto out; + } + /* unacceptable, something is wrong, fail */ + algo_name = OBJ_nid2sn(mdnid); + failf(data, "Unable to find digest algorithm %s (NID %d) " + "for channel binding", algo_name ? algo_name : "(null)", mdnid); result = CURLE_SSL_INVALIDCERTSTATUS; - goto error; + goto out; + } + + if(!X509_digest(cert, algo_type, buf, &length)) { + failf(data, "X509_digest() failed for channel binding"); + result = CURLE_SSL_INVALIDCERTSTATUS; + goto out; } /* Append "tls-server-end-point:" */ result = curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1); if(result) - goto error; + goto out; /* Append digest */ result = curlx_dyn_addn(binding, buf, length); -error: +out: X509_free(cert); return result; }
diff --git a/Utilities/cmcurl/lib/vtls/openssl.h b/Utilities/cmcurl/lib/vtls/openssl.h index cfffe9d..61d4a17 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.h +++ b/Utilities/cmcurl/lib/vtls/openssl.h
@@ -26,6 +26,40 @@ #include "curl_setup.h" #ifdef USE_OPENSSL + +#ifdef USE_WIN32_CRYPTO +#include <wincrypt.h> +/* If <wincrypt.h> is included directly, or indirectly via <schannel.h>, + * <winldap.h>, <iphlpapi.h>, or something else, <wincrypt.h> does this: + * #define X509_NAME ((LPCSTR)7) + * + * In BoringSSL/AWC-LC's <openssl/base.h> there is: + * typedef struct X509_name_st X509_NAME; + * etc. + * + * The redefined symbols break these OpenSSL headers when included after + * <wincrypt.h>. + * The workaround is to undefine those defines here (and only here). + * + * For unity builds it may need to be repeated elsewhere too, e.g. in ldap.c, + * to apply to other sources using OpenSSL includes. Each compilation unit + * needs undefine them between the first <wincrypt.h> include and the first + * OpenSSL include. + * + * OpenSSL does this in <openssl/ssl.h> and <openssl/x509v3.h>, but it + * also does the #undef by including <openssl/ossl_typ.h>. <3.1.0 only does + * it on the first include. + * + * LibreSSL automatically undefines these symbols before using them. + */ +#undef X509_NAME +#undef X509_EXTENSIONS +#undef PKCS7_ISSUER_AND_SERIAL +#undef PKCS7_SIGNER_INFO +#undef OCSP_REQUEST +#undef OCSP_RESPONSE +#endif /* USE_WIN32_CRYPTO */ + /* * This header should only be needed to get included by vtls.c, openssl.c * and ngtcp2.c @@ -113,6 +147,9 @@ void *ssl_user_data, Curl_ossl_init_session_reuse_cb *sess_reuse_cb); +/* Is a resolved HTTPS-RR needed for initializing OpenSSL? */ +bool Curl_ossl_need_httpsrr(struct Curl_easy *data); + #ifndef HAVE_OPENSSL3 #define SSL_get1_peer_certificate SSL_get_peer_certificate #endif @@ -121,7 +158,7 @@ /** * Setup the OpenSSL X509_STORE in `ssl_ctx` for the cfilter `cf` and - * easy handle `data`. Will allow reuse of a shared cache if suitable + * easy handle `data`. Allows reuse of a shared cache if suitable * and configured. */ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c index 061b444..d886c85 100644 --- a/Utilities/cmcurl/lib/vtls/rustls.c +++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -32,6 +32,7 @@ #include "curlx/fopen.h" #include "curlx/strerr.h" #include "urldata.h" +#include "cf-dns.h" #include "curl_trc.h" #include "httpsrr.h" #include "vtls/vtls.h" @@ -40,6 +41,9 @@ #include "vtls/keylog.h" #include "vtls/cipher_suite.h" #include "vtls/x509asn1.h" +#ifdef USE_ECH +#include "curlx/base64.h" +#endif struct rustls_ssl_backend_data { const struct rustls_client_config *config; @@ -294,8 +298,8 @@ * we get either an error or EAGAIN/EWOULDBLOCK. * * it is okay to call this function with plainbuf == NULL and plainlen == 0. - * In that case, it will not read anything into Rustls' plaintext input buffer. - * It will only drain Rustls' plaintext output buffer into the socket. + * In that case, it does not read anything into Rustls' plaintext input buffer. + * It only drains Rustls' plaintext output buffer into the socket. */ static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *plainbuf, size_t plainlen, @@ -369,7 +373,7 @@ *pnwritten += (ssize_t)plainwritten; out: - CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zd", + CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zu", plainlen, result, *pnwritten); return result; } @@ -570,7 +574,7 @@ } #ifdef USE_ECH - if(ECH_ENABLED(data)) { + if(CURLECH_ENABLED(data)) { tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3; tls_versions_len = 1; infof(data, "rustls: ECH enabled, forcing TLSv1.3"); @@ -906,16 +910,26 @@ } #ifdef USE_ECH + +static bool cr_ech_need_httpsrr(struct Curl_easy *data) +{ + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +} + static CURLcode init_config_builder_ech(struct Curl_easy *data, - const struct ssl_connect_data *connssl, + struct Curl_cfilter *cf, struct rustls_client_config_builder *builder) { const rustls_hpke *hpke = rustls_supported_hpke(); unsigned char *ech_config = NULL; size_t ech_config_len = 0; struct Curl_dns_entry *dns = NULL; - struct Curl_https_rrinfo *rinfo = NULL; CURLcode result = CURLE_OK; rustls_result rr; @@ -960,16 +974,9 @@ } } else { - if(connssl->peer.hostname) { - dns = Curl_dnscache_get(data, connssl->peer.hostname, - connssl->peer.port, data->conn->ip_version); - } - if(!dns) { - failf(data, "rustls: ECH requested but no DNS info available"); - result = CURLE_SSL_CONNECT_ERROR; - goto cleanup; - } - rinfo = dns->hinfo; + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); + if(!rinfo || !rinfo->echconfiglist) { failf(data, "rustls: ECH requested but no ECHConfig available"); result = CURLE_SSL_CONNECT_ERROR; @@ -994,7 +1001,7 @@ curlx_free(ech_config); } if(dns) { - Curl_resolv_unlink(data, &dns); + Curl_dns_entry_unlink(data, &dns); } return result; } @@ -1065,8 +1072,8 @@ } #ifdef USE_ECH - if(ECH_ENABLED(data)) { - result = init_config_builder_ech(data, connssl, config_builder); + if(CURLECH_ENABLED(data)) { + result = init_config_builder_ech(data, cf, config_builder); if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) { rustls_client_config_builder_free(config_builder); return result; @@ -1117,7 +1124,7 @@ /* Given an established network connection, do a TLS handshake. * - * This function will set `*done` to true once the handshake is complete. + * This function sets `*done` to true once the handshake is complete. * This function never reads the value of `*done*`. */ static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -1128,22 +1135,33 @@ (struct rustls_ssl_backend_data *)connssl->backend; const struct rustls_connection *rconn = NULL; CURLcode tmperr = CURLE_OK; - int result; + CURLcode result; bool wants_read; bool wants_write; + ssize_t nread; DEBUGASSERT(backend); CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state); *done = FALSE; - if(!backend->conn) { - result = cr_init_backend(cf, data, - (struct rustls_ssl_backend_data *)connssl->backend); - CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); - if(result != CURLE_OK) { - return result; +#ifdef USE_ECH + /* if we do ECH and need the HTTPS-RR information for it, + * we delay the connect until it arrives or DNS resolve fails. */ + if(cr_ech_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); + return CURLE_OK; } +#endif /* USE_ECH */ + + if(!backend->conn) { + result = + cr_init_backend(cf, data, + (struct rustls_ssl_backend_data *)connssl->backend); + CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result); + if(result) + return result; connssl->state = ssl_connection_negotiating; } rconn = backend->conn; @@ -1199,7 +1217,7 @@ while(rustls_connection_get_peer_certificate(rconn, num_certs)) { num_certs++; if(num_certs > MAX_ALLOWED_CERT_AMOUNT) { - failf(data, "%zu certificates is more than allowed (%u)", + failf(data, "%zu certificates is more than allowed (%d)", num_certs, MAX_ALLOWED_CERT_AMOUNT); return CURLE_SSL_CONNECT_ERROR; } @@ -1263,7 +1281,13 @@ if(wants_read) { CURL_TRC_CF(data, cf, "rustls_connection wants us to read_tls."); - if(tls_recv_more(cf, data, &tmperr) < 0) { + nread = tls_recv_more(cf, data, &tmperr); + if(nread == 0) { + connssl->peer_closed = TRUE; + failf(data, "TLS connect error: Connection closed abruptly"); + return CURLE_SSL_CONNECT_ERROR; + } + if(nread < 0) { if(tmperr == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "reading would block"); connssl->io_need = CURL_SSL_IO_NEED_RECV;
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c index 650a0e3..fb5ff0e 100644 --- a/Utilities/cmcurl/lib/vtls/schannel.c +++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -175,20 +175,20 @@ for(; i <= (ssl_version_max >> 16); ++i) { switch(i) { case CURL_SSLVERSION_TLSv1_0: - (*enabled_protocols) |= SP_PROT_TLS1_0_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_0_CLIENT; break; case CURL_SSLVERSION_TLSv1_1: - (*enabled_protocols) |= SP_PROT_TLS1_1_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_1_CLIENT; break; case CURL_SSLVERSION_TLSv1_2: - (*enabled_protocols) |= SP_PROT_TLS1_2_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_2_CLIENT; break; case CURL_SSLVERSION_TLSv1_3: /* Windows Server 2022 and newer */ if(curlx_verify_windows_version(10, 0, 20348, PLATFORM_WINNT, VERSION_GREATER_THAN_EQUAL)) { - (*enabled_protocols) |= SP_PROT_TLS1_3_CLIENT; + *enabled_protocols |= SP_PROT_TLS1_3_CLIENT; break; } else { /* Windows 10 and older */ @@ -277,7 +277,7 @@ return 0; /* not found */ } -#define NUM_CIPHERS 47 /* There are 47 options listed above */ +#define NUM_CIPHERS 47 /* There are a maximum of 47 options listed above */ static CURLcode set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, ALG_ID *algIds) @@ -636,7 +636,7 @@ } else { /* Pre-Windows 10 1809 or the user set a legacy algorithm list. - Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. */ + Schannel does not negotiate TLS 1.3 when SCHANNEL_CRED is used. */ ALG_ID algIds[NUM_CIPHERS]; char *ciphers = conn_config->cipher_list; SCHANNEL_CRED schannel_cred = { 0 }; @@ -914,18 +914,18 @@ unsigned short *list_len = NULL; struct alpn_proto_buf proto; - /* The first four bytes will be an unsigned int indicating number + /* The first four bytes is an unsigned int indicating number of bytes of data in the rest of the buffer. */ extension_len = (unsigned int *)(void *)(&alpn_buffer[cur]); cur += (int)sizeof(unsigned int); - /* The next four bytes are an indicator that this buffer will contain + /* The next four bytes are an indicator that this buffer contains ALPN data, as opposed to NPN, for example. */ *(unsigned int *)(void *)&alpn_buffer[cur] = SecApplicationProtocolNegotiationExt_ALPN; cur += (int)sizeof(unsigned int); - /* The next two bytes will be an unsigned short indicating the number + /* The next two bytes is an unsigned short indicating the number of bytes used to list the preferred protocols. */ list_len = (unsigned short *)(void *)(&alpn_buffer[cur]); cur += (int)sizeof(unsigned short); @@ -994,7 +994,7 @@ if(sspi_status != SEC_I_CONTINUE_NEEDED) { char buffer[STRERROR_LEN]; - Curl_safefree(backend->ctxt); + curlx_safefree(backend->ctxt); switch(sspi_status) { case SEC_E_INSUFFICIENT_MEMORY: failf(data, "schannel: initial InitializeSecurityContext failed: %s", @@ -1330,12 +1330,12 @@ memcpy(inbuf[0].pvBuffer, backend->encdata.buffer, backend->encdata.offset); - /* The socket must be writeable (or a poll error occurred) before we call + /* The socket must be writable (or a poll error occurred) before we call InitializeSecurityContext to continue processing the received TLS records. This is because that function is not idempotent and we do not support partial save/resume sending replies of handshake tokens. */ if(!SOCKET_WRITABLE(Curl_conn_cf_get_socket(cf, data), 0)) { - SCH_DEV(infof(data, "schannel: handshake waiting for writeable socket")); + SCH_DEV(infof(data, "schannel: handshake waiting for writable socket")); connssl->io_need = CURL_SSL_IO_NEED_SEND; curlx_free(inbuf[0].pvBuffer); return CURLE_OK; @@ -1348,7 +1348,7 @@ &outbuf_desc, &backend->ret_flags, NULL); /* free buffer for received handshake data */ - Curl_safefree(inbuf[0].pvBuffer); + curlx_safefree(inbuf[0].pvBuffer); /* check if the handshake was incomplete */ switch(sspi_status) { @@ -1394,7 +1394,7 @@ case SEC_I_INCOMPLETE_CREDENTIALS: if(!(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { /* If the server has requested a client certificate, attempt to - continue the handshake without one. This will allow connections to + continue the handshake without one. This allows connections to servers which request a client certificate but do not require it. */ backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; @@ -1475,7 +1475,7 @@ } /* Verify the hostname manually when certificate verification is disabled, - because in that case Schannel will not verify it. */ + because in that case Schannel does not verify it. */ if(!conn_config->verifypeer && conn_config->verifyhost) return Curl_verify_host(cf, data); @@ -1568,7 +1568,7 @@ CertCloseStore(cred->client_cert_store, 0); cred->client_cert_store = NULL; } - Curl_safefree(cred); + curlx_safefree(cred); } } } @@ -1670,7 +1670,7 @@ traverse_cert_store(ccert_context, cert_counter_callback, &certs_count); if(certs_count > MAX_ALLOWED_CERT_AMOUNT) { - failf(data, "%d certificates is more than allowed (%u)", + failf(data, "%d certificates is more than allowed (%d)", certs_count, MAX_ALLOWED_CERT_AMOUNT); CertFreeCertificateContext(ccert_context); return CURLE_SSL_CONNECT_ERROR; @@ -1757,7 +1757,13 @@ SCH_RENEG_CALLER_IS_SEND }; -#define MAX_RENEG_BLOCK_TIME (7 * 1000) /* 7 seconds in milliseconds */ +/* The maximum time we allow for Schannel renegotiation which may in some + rare cases block either due to libcurl (waiting on the socket) or Windows + (waiting on an interactive security prompt). Note Schannel "renegotiation" + is not necessarily literal TLS renegotiation, but means DecryptMessage + returned SEC_I_RENEGOTIATE which means at least the security context needs + to be re-established. */ +#define MAX_RENEG_BLOCK_TIME (60 * 1000) /* 60 seconds in milliseconds */ /* This function renegotiates the connection due to a server request received by schannel_recv. This function returns CURLE_AGAIN if the renegotiation is @@ -1845,11 +1851,11 @@ * occur if the user is waiting on the socket only in one direction. * * For example, if the user has called recv then they may not be waiting - * for a writeable socket and vice versa, so we block to avoid that. + * for a writable socket and vice versa, so we block to avoid that. * * In practice a wait is unlikely to occur. For caller recv if handshake - * data needs to be sent then we block for a writeable socket that should - * be writeable immediately except for OS resource constraints. For caller + * data needs to be sent then we block for a writable socket that should + * be writable immediately except for OS resource constraints. For caller * send if handshake data needs to be received then we block for a readable * socket, which could take some time, but it is more likely the user has * called recv since they had called it prior (only recv can start @@ -1905,7 +1911,7 @@ SCH_DEV(infof(data, "schannel: renegotiation wait until socket is" "%s%s for up to %" FMT_TIMEDIFF_T " ms", ((readfd != CURL_SOCKET_BAD) ? " readable" : ""), - ((writefd != CURL_SOCKET_BAD) ? " writeable" : ""), + ((writefd != CURL_SOCKET_BAD) ? " writable" : ""), timeout_ms)); what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, timeout_ms); @@ -2028,7 +2034,7 @@ sent. The unwritten encrypted bytes would be the first bytes to send on the next invocation. Here's the catch with this - if we tell the client that all the - bytes have been sent, will the client call this method again to + bytes have been sent, does the client call this method again to send the buffered data? Looking at who calls this function, it seems the answer is NO. */ @@ -2082,7 +2088,7 @@ result = CURLE_SEND_ERROR; } - Curl_safefree(ptr); + curlx_safefree(ptr); if(len == *pnwritten) /* Encrypted message including header, data and trailer entirely sent. @@ -2550,7 +2556,7 @@ if(backend->ctxt) { DEBUGF(infof(data, "schannel: clear security context handle")); Curl_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); - Curl_safefree(backend->ctxt); + curlx_safefree(backend->ctxt); } /* free SSPI Schannel API credential handle */ @@ -2563,7 +2569,7 @@ /* free internal buffer for received encrypted data */ if(backend->encdata.buffer) { - Curl_safefree(backend->encdata.buffer); + curlx_safefree(backend->encdata.buffer); backend->encdata.length = 0; backend->encdata.offset = 0; backend->encdata_is_incomplete = FALSE; @@ -2571,7 +2577,7 @@ /* free internal buffer for received decrypted data */ if(backend->decdata.buffer) { - Curl_safefree(backend->decdata.buffer); + curlx_safefree(backend->decdata.buffer); backend->decdata.length = 0; backend->decdata.offset = 0; }
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c index 414b2e9..9be6fe3 100644 --- a/Utilities/cmcurl/lib/vtls/schannel_verify.c +++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -262,7 +262,7 @@ /* * Read the CA file completely into memory before parsing it. This - * optimizes for the common case where the CA file will be relatively + * optimizes for the common case where the CA file is relatively * small ( < 1 MiB ). */ ca_file_handle = curlx_CreateFile(ca_file, @@ -290,7 +290,7 @@ } if(file_size.QuadPart > MAX_CAFILE_SIZE) { - failf(data, "schannel: CA file exceeds max size of %u bytes", + failf(data, "schannel: CA file exceeds max size of %d bytes", MAX_CAFILE_SIZE); result = CURLE_SSL_CACERT_BADFILE; goto cleanup; @@ -336,7 +336,7 @@ if(ca_file_handle != INVALID_HANDLE_VALUE) { CloseHandle(ca_file_handle); } - Curl_safefree(ca_file_buffer); + curlx_safefree(ca_file_buffer); return result; } @@ -366,7 +366,7 @@ #ifndef CERT_NAME_SEARCH_ALL_NAMES_FLAG #define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x2 #endif - /* CertGetNameString will provide the 8-bit character string without + /* CertGetNameString provides the 8-bit character string without * any decoding */ DWORD name_flags = CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG; @@ -438,13 +438,12 @@ static bool get_num_host_info(struct num_ip_data *ip_blob, LPCSTR hostname) { - bool result = FALSE; struct in_addr ia; int res = curlx_inet_pton(AF_INET, hostname, &ia); if(res) { ip_blob->size = sizeof(struct in_addr); memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr)); - result = TRUE; + return TRUE; } else { struct in6_addr ia6; @@ -452,10 +451,10 @@ if(res) { ip_blob->size = sizeof(struct in6_addr); memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr)); - result = TRUE; + return TRUE; } } - return result; + return FALSE; } static bool get_alt_name_info(struct Curl_easy *data, @@ -463,20 +462,19 @@ PCERT_ALT_NAME_INFO *alt_name_info, LPDWORD alt_name_info_size) { - bool result = FALSE; PCERT_INFO cert_info = NULL; PCERT_EXTENSION extension = NULL; CRYPT_DECODE_PARA decode_para = { sizeof(CRYPT_DECODE_PARA), NULL, NULL }; if(!ctx) { failf(data, "schannel: Null certificate context."); - return result; + return FALSE; } cert_info = ctx->pCertInfo; if(!cert_info) { failf(data, "schannel: Null certificate info."); - return result; + return FALSE; } extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, @@ -484,7 +482,7 @@ cert_info->rgExtension); if(!extension) { failf(data, "schannel: CertFindExtension() returned no extension."); - return result; + return FALSE; } if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, @@ -496,11 +494,10 @@ alt_name_info, alt_name_info_size)) { failf(data, "schannel: CryptDecodeObjectEx() returned no alternate name " - "information."); - return result; + "information."); + return FALSE; } - result = TRUE; - return result; + return TRUE; } /* Verify the server's hostname */ @@ -572,7 +569,7 @@ goto cleanup; } - /* CertGetNameString guarantees that the returned name will not contain + /* CertGetNameString guarantees that the returned name does not contain * embedded null bytes. This appears to be undocumented behavior. */ cert_hostname_buff = (LPTSTR)curlx_malloc(len * sizeof(TCHAR)); @@ -650,7 +647,7 @@ cleanup: LocalFree(alt_name_info); - Curl_safefree(cert_hostname_buff); + curlx_safefree(cert_hostname_buff); if(pCertContextServer) CertFreeCertificateContext(pCertContextServer); @@ -763,7 +760,7 @@ else engine_config.cbSize = sizeof(struct cert_chain_engine_config_win7); - /* CertCreateCertificateChainEngine will check the expected size of the + /* CertCreateCertificateChainEngine checks the expected size of the * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size * does not match the expected size. When this occurs, it indicates that * CAINFO is not supported on the version of Windows in use.
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c index f7201d1..891facf 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.c +++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -50,6 +50,7 @@ #include "vtls/vtls.h" /* generic SSL protos etc */ #include "vtls/vtls_int.h" #include "vtls/vtls_scache.h" +#include "vtls/keylog.h" #include "vtls/openssl.h" /* OpenSSL versions */ #include "vtls/gtls.h" /* GnuTLS versions */ @@ -272,22 +273,22 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc) { - Curl_safefree(sslc->CApath); - Curl_safefree(sslc->CAfile); - Curl_safefree(sslc->issuercert); - Curl_safefree(sslc->clientcert); - Curl_safefree(sslc->cipher_list); - Curl_safefree(sslc->cipher_list13); - Curl_safefree(sslc->pinned_key); - Curl_safefree(sslc->cert_blob); - Curl_safefree(sslc->ca_info_blob); - Curl_safefree(sslc->issuercert_blob); - Curl_safefree(sslc->curves); - Curl_safefree(sslc->signature_algorithms); - Curl_safefree(sslc->CRLfile); + curlx_safefree(sslc->CApath); + curlx_safefree(sslc->CAfile); + curlx_safefree(sslc->issuercert); + curlx_safefree(sslc->clientcert); + curlx_safefree(sslc->cipher_list); + curlx_safefree(sslc->cipher_list13); + curlx_safefree(sslc->pinned_key); + curlx_safefree(sslc->cert_blob); + curlx_safefree(sslc->ca_info_blob); + curlx_safefree(sslc->issuercert_blob); + curlx_safefree(sslc->curves); + curlx_safefree(sslc->signature_algorithms); + curlx_safefree(sslc->CRLfile); #ifdef USE_TLS_SRP - Curl_safefree(sslc->username); - Curl_safefree(sslc->password); + curlx_safefree(sslc->username); + curlx_safefree(sslc->password); #endif } @@ -395,7 +396,7 @@ CURLcode Curl_ssl_conn_config_init(struct Curl_easy *data, struct connectdata *conn) { - /* Clone "primary" SSL configurations from the esay handle to + /* Clone "primary" SSL configurations from the easy handle to * the connection. They are used for connection cache matching and * probably outlive the easy handle */ if(!clone_ssl_primary_config(&data->set.ssl.primary, &conn->ssl_config)) @@ -520,7 +521,7 @@ static void cf_ctx_free(struct ssl_connect_data *ctx) { if(ctx) { - Curl_safefree(ctx->negotiated.alpn); + curlx_safefree(ctx->negotiated.alpn); Curl_bufq_free(&ctx->earlydata); curlx_free(ctx->backend); curlx_free(ctx); @@ -787,7 +788,7 @@ encode = curlx_base64_encode(sha256sumdigest, CURL_SHA256_DIGEST_LENGTH, &cert_hash, &cert_hash_len); - Curl_safefree(sha256sumdigest); + curlx_safefree(sha256sumdigest); if(encode) return encode; @@ -802,7 +803,7 @@ pinned_hash_len = end_pos ? (size_t)(end_pos - pinned_hash) : strlen(pinned_hash); - /* compare base64 sha256 digests" */ + /* compare base64 sha256 digests */ if(cert_hash_len == pinned_hash_len && !memcmp(cert_hash, pinned_hash, cert_hash_len)) { DEBUGF(infof(data, "public key hash matches pinned value")); @@ -815,14 +816,14 @@ /* next one or we are at the end */ pinned_hash = end_pos ? (end_pos + 1) : NULL; } - Curl_safefree(cert_hash); + curlx_safefree(cert_hash); } else { long filesize; size_t size, pem_len; CURLcode pem_read; struct dynbuf buf; - char unsigned *pem_ptr = NULL; + unsigned char *pem_ptr = NULL; size_t left; FILE *fp = curlx_fopen(pinnedpubkey, "rb"); if(!fp) @@ -869,7 +870,7 @@ } /* - * Otherwise we will assume it is PEM and try to decode it after placing + * Otherwise we assume it is PEM and try to decode it after placing * null-terminator */ pem_read = pubkey_pem_to_der(curlx_dyn_ptr(&buf), &pem_ptr, &pem_len); @@ -885,7 +886,7 @@ result = CURLE_OK; end: curlx_dyn_free(&buf); - Curl_safefree(pem_ptr); + curlx_safefree(pem_ptr); curlx_fclose(fp); } @@ -1179,12 +1180,12 @@ void Curl_ssl_peer_cleanup(struct ssl_peer *peer) { - Curl_safefree(peer->sni); + curlx_safefree(peer->sni); if(peer->dispname != peer->hostname) curlx_free(peer->dispname); peer->dispname = NULL; - Curl_safefree(peer->hostname); - Curl_safefree(peer->scache_key); + curlx_safefree(peer->hostname); + curlx_safefree(peer->scache_key); peer->type = CURL_SSL_PEER_DNS; } @@ -1221,7 +1222,7 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, const char *tls_id, - int transport) + uint8_t transport) { const char *ehostname, *edispname; CURLcode result = CURLE_OUT_OF_MEMORY; @@ -1248,7 +1249,7 @@ { ehostname = cf->conn->host.name; edispname = cf->conn->host.dispname; - peer->port = cf->conn->remote_port; + peer->port = (uint16_t)cf->conn->remote_port; } /* hostname MUST exist and not be empty */ @@ -1367,6 +1368,13 @@ if(connssl->state == ssl_connection_complete) { connssl->handshake_done = *Curl_pgrs_now(data); } + if(Curl_tls_keylog_enabled()) { + infof(data, "SSLKEYLOGFILE set, all TLS secrets are logged to '%s'", + Curl_tls_keylog_file_name()); +#ifdef LIBRESSL_VERSION_NUMBER + infof(data, "Note LibreSSL only supports SSLKEYLOGFILE for TLS <= 1.2"); +#endif + } /* Connection can be deferred when sending early data */ DEBUGASSERT(connssl->state == ssl_connection_complete || connssl->state == ssl_connection_deferred); @@ -1393,7 +1401,7 @@ if(blen > connssl->earlydata_max) blen = connssl->earlydata_max; result = Curl_bufq_write(&connssl->earlydata, buf, blen, &nwritten); - CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd", + CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zu", blen, nwritten); if(result) return result; @@ -1455,16 +1463,16 @@ { struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; - bool result; + bool pending; CF_DATA_SAVE(save, cf, data); if(connssl->ssl_impl->data_pending && connssl->ssl_impl->data_pending(cf, data)) - result = TRUE; + pending = TRUE; else - result = cf->next->cft->has_data_pending(cf->next, data); + pending = cf->next->cft->has_data_pending(cf->next, data); CF_DATA_RESTORE(cf, save); - return result; + return pending; } static CURLcode ssl_cf_send(struct Curl_cfilter *cf, @@ -1684,7 +1692,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = { "SSL-PROXY", - CF_TYPE_SSL|CF_TYPE_PROXY, + CF_TYPE_SSL | CF_TYPE_PROXY, CURL_LOG_LVL_NONE, ssl_cf_destroy, ssl_cf_connect,
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h index b9335bb..6db67cf 100644 --- a/Utilities/cmcurl/lib/vtls/vtls.h +++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -49,8 +49,14 @@ #define SSLSUPP_ISSUERCERT_BLOB (1 << 14) /* CURLOPT_ISSUERCERT_BLOB */ #ifdef USE_ECH -#include "curlx/base64.h" -#define ECH_ENABLED(data) \ +/* CURLECH_ bits for the tls_ech option */ +#define CURLECH_DISABLE (1 << 0) +#define CURLECH_GREASE (1 << 1) +#define CURLECH_ENABLE (1 << 2) +#define CURLECH_HARD (1 << 3) +#define CURLECH_CLA_CFG (1 << 4) + +#define CURLECH_ENABLED(data) \ ((data)->set.tls_ech && !((data)->set.tls_ech & CURLECH_DISABLE)) #endif /* USE_ECH */ @@ -90,8 +96,8 @@ char *sni; /* SNI version of hostname or NULL if not usable */ char *scache_key; /* for lookups in session cache */ ssl_peer_type type; /* type of the peer information */ - int port; /* port we are talking to */ - int transport; /* one of TRNSPRT_* defines */ + uint16_t port; /* port we are talking to */ + uint8_t transport; /* one of TRNSPRT_* defines */ }; CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, @@ -134,7 +140,7 @@ bool proxy); /* Update certain connection SSL config flags after they have - * been changed on the easy handle. Will work for `verifypeer`, + * been changed on the easy handle. Works for `verifypeer`, * `verifyhost` and `verifystatus`. */ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); @@ -144,7 +150,7 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, const char *tls_id, - int transport); + uint8_t transport); /** * Free all allocated data and reset peer information. */
diff --git a/Utilities/cmcurl/lib/vtls/vtls_scache.c b/Utilities/cmcurl/lib/vtls/vtls_scache.c index 673abcf..59fa256 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_scache.c +++ b/Utilities/cmcurl/lib/vtls/vtls_scache.c
@@ -200,7 +200,7 @@ } if(ssl->version || ssl->version_max) { - r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version, + r = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version, (ssl->version_max >> 16)); if(r) goto out; @@ -238,11 +238,6 @@ r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local); if(r) goto out; - if(ssl->cert_blob) { - r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); - if(r) - goto out; - } if(ssl->ca_info_blob) { r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob); if(r) @@ -254,6 +249,11 @@ goto out; } } + if(ssl->cert_blob) { + r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob); + if(r) + goto out; + } if(ssl->pinned_key && ssl->pinned_key[0]) { r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key); if(r) @@ -301,6 +301,7 @@ size_t peer_count; int default_lifetime_secs; long age; + BIT(is_locked); }; static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data) @@ -401,12 +402,12 @@ peer->sobj = NULL; } peer->sobj_free = NULL; - Curl_safefree(peer->clientcert); + curlx_safefree(peer->clientcert); #ifdef USE_TLS_SRP - Curl_safefree(peer->srp_username); - Curl_safefree(peer->srp_password); + curlx_safefree(peer->srp_username); + curlx_safefree(peer->srp_password); #endif - Curl_safefree(peer->ssl_peer_key); + curlx_safefree(peer->ssl_peer_key); peer->age = 0; peer->hmac_set = FALSE; } @@ -583,15 +584,26 @@ /* Lock shared SSL session data */ void Curl_ssl_scache_lock(struct Curl_easy *data) { - if(CURL_SHARE_ssl_scache(data)) - Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE); + struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); + if(scache) { + if(CURL_SHARE_ssl_scache(data)) + Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_ACCESS_SINGLE); + DEBUGASSERT(!scache->is_locked); + scache->is_locked = TRUE; + } } /* Unlock shared SSL session data */ void Curl_ssl_scache_unlock(struct Curl_easy *data) { - if(CURL_SHARE_ssl_scache(data)) - Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); + struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); + if(scache) { + DEBUGASSERT(scache->is_locked); + scache->is_locked = FALSE; + if(CURL_SHARE_ssl_scache(data)) + Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION); + } } static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, @@ -734,12 +746,19 @@ peer = cf_ssl_get_free_peer(scache); if(peer) { + char buffer[64]; const char *ccert = conn_config ? conn_config->clientcert : NULL; const char *username = NULL, *password = NULL; #ifdef USE_TLS_SRP username = conn_config ? conn_config->username : NULL; password = conn_config ? conn_config->password : NULL; #endif + if(!ccert && conn_config && conn_config->cert_blob) { + /* when using a client cert blob, create a name for it */ + curl_msnprintf(buffer, sizeof(buffer), + "cert-%p", conn_config->cert_blob->data); + ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */ + } result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, username, password, NULL, NULL); if(result) @@ -993,6 +1012,12 @@ #define CURL_SSL_TICKET_MAX (16 * 1024) +bool Curl_ssl_scache_is_locked(struct Curl_easy *data) +{ + struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); + return scache && scache->is_locked; +} + static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer) { CURLcode result; @@ -1075,49 +1100,47 @@ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data); struct Curl_ssl_scache_peer *peer = NULL; struct Curl_ssl_session *s = NULL; - bool locked = FALSE; - CURLcode r; + CURLcode result; if(!scache) { - r = CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } if(!ssl_peer_key && (!shmac || !shmac_len)) { - r = CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } - r = Curl_ssl_session_unpack(data, sdata, sdata_len, &s); - if(r) + result = Curl_ssl_session_unpack(data, sdata, sdata_len, &s); + if(result) goto out; Curl_ssl_scache_lock(data); - locked = TRUE; if(ssl_peer_key) { - r = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer); - if(r) + result = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer); + if(result) goto out; } else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) { /* Either salt+hmac was garbled by caller or is from a curl version * that does things differently */ - r = CURLE_BAD_FUNCTION_ARGUMENT; + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; } else { const unsigned char *salt = shmac; const unsigned char *hmac = shmac + sizeof(peer->key_salt); - r = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer); - if(r) + result = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer); + if(result) goto out; if(!peer) { peer = cf_ssl_get_free_peer(scache); if(peer) { - r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, - NULL, NULL, salt, hmac); - if(r) + result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, + NULL, NULL, salt, hmac); + if(result) goto out; } } @@ -1133,10 +1156,10 @@ } out: - if(locked) + if(scache && scache->is_locked) Curl_ssl_scache_unlock(data); Curl_ssl_session_destroy(s); - return r; + return result; } CURLcode Curl_ssl_session_export(struct Curl_easy *data, @@ -1156,8 +1179,6 @@ if(!export_fn) return CURLE_BAD_FUNCTION_ARGUMENT; - if(!scache) - return CURLE_OK; Curl_ssl_scache_lock(data);
diff --git a/Utilities/cmcurl/lib/vtls/vtls_scache.h b/Utilities/cmcurl/lib/vtls/vtls_scache.h index b9db0f1..a6a36f1 100644 --- a/Utilities/cmcurl/lib/vtls/vtls_scache.h +++ b/Utilities/cmcurl/lib/vtls/vtls_scache.h
@@ -52,7 +52,7 @@ /* Create a key from peer and TLS configuration information that is * unique for how the connection filter wants to establish a TLS * connection to the peer. - * If the filter is a TLS proxy filter, it will use the proxy relevant + * If the filter is a TLS proxy filter, it uses the proxy relevant * information. * @param cf the connection filter wanting to use it * @param peer the peer the filter wants to talk to @@ -84,7 +84,7 @@ /* Unlock session cache mutex */ void Curl_ssl_scache_unlock(struct Curl_easy *data); -/* Get TLS session object from the cache for the ssl_peer_ey. +/* Get TLS session object from the cache for the ssl_peer_key. * scache mutex must be locked (see Curl_ssl_scache_lock). * Caller must make sure that the ownership of returned session object * is properly taken (e.g. its refcount is incremented @@ -112,7 +112,7 @@ * @param data the transfer involved * @param ssl_peer_key the key for lookup * @param sobj the TLS session object - * @param sobj_free_cb callback to free the session objectt + * @param sobj_free_cb callback to free the session object */ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -199,6 +199,8 @@ #ifdef USE_SSLS_EXPORT +bool Curl_ssl_scache_is_locked(struct Curl_easy *data); + CURLcode Curl_ssl_session_import(struct Curl_easy *data, const char *ssl_peer_key, const unsigned char *shmac, size_t shmac_len,
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c index aa841a7..15c81c2 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.c +++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -33,8 +33,8 @@ #include <wolfssl/options.h> #include <wolfssl/version.h> -#if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */ -#error "wolfSSL version should be at least 3.4.6" +#if LIBWOLFSSL_VERSION_HEX < 0x05000000 /* wolfSSL 5.0.0 (2021-11-01) */ +#error "wolfSSL version should be at least 5.0.0" #endif #if defined(OPENSSL_COEXIST) && LIBWOLFSSL_VERSION_HEX < 0x05007006 #error "wolfSSL 5.7.6 or newer is required to coexist with OpenSSL" @@ -56,6 +56,7 @@ #include "urldata.h" #include "curl_trc.h" #include "httpsrr.h" +#include "cf-dns.h" #include "vtls/vtls.h" #include "vtls/vtls_int.h" #include "vtls/vtls_scache.h" @@ -65,6 +66,9 @@ #include "curlx/strdup.h" #include "curlx/strcopy.h" #include "vtls/x509asn1.h" +#ifdef USE_ECH +#include "curlx/base64.h" +#endif #include <wolfssl/ssl.h> #include <wolfssl/error-ssl.h> @@ -109,7 +113,7 @@ * Availability note: * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in * wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that - * option is not set, then TLS 1.3 will not be logged. + * option is not set, then TLS 1.3 is not logged. * For TLS 1.2 and before, we use wolfSSL_get_keys(). * wolfSSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA * (--enable-opensslextra or --enable-all). @@ -168,7 +172,6 @@ unsigned char *ms, *sr, *cr; unsigned int msLen, srLen, crLen, i, x = 0; -#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */ /* wolfSSL_GetVersion is available since 3.13, we use it instead of * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or * --enable-all). Failing to perform this check could result in an unusable @@ -184,7 +187,6 @@ * is not directly usable. */ return; } -#endif if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != WOLFSSL_SUCCESS) { @@ -546,7 +548,7 @@ infof(data, "SSL reusing session with ALPN '%s'", scs->alpn ? scs->alpn : "-"); if(ssl_config->earlydata && - !cf->conn->connect_only && + !cf->conn->bits.connect_only && !strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) { bool do_early_data = FALSE; if(sess_reuse_cb) { @@ -872,8 +874,7 @@ } #endif -/* 4.2.0 (2019) */ -#if LIBWOLFSSL_VERSION_HEX < 0x04002000 || !defined(OPENSSL_EXTRA) +#ifndef OPENSSL_EXTRA static int wssl_legacy_CTX_set_min_proto_version(WOLFSSL_CTX *ctx, int version) { int res; @@ -909,11 +910,11 @@ } #define wolfSSL_CTX_set_min_proto_version wssl_legacy_CTX_set_min_proto_version #define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version -#endif +#endif /* OPENSSL_EXTRA */ -static CURLcode client_certificate(struct Curl_easy *data, - struct ssl_config_data *ssl_config, - struct wssl_ctx *wctx) +static CURLcode wssl_client_cert(struct Curl_easy *data, + struct ssl_config_data *ssl_config, + struct wssl_ctx *wctx) { /* Load the client certificate, and private key */ #ifndef NO_FILESYSTEM @@ -1076,80 +1077,29 @@ } #define QUIC_GROUPS "P-256:P-384:P-521" - -CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, - struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_peer *peer, - const struct alpn_spec *alpns_requested, - Curl_wssl_ctx_setup_cb *cb_setup, - void *cb_user_data, - void *ssl_user_data, - Curl_wssl_init_session_reuse_cb *sess_reuse_cb) -{ - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_primary_config *conn_config; - WOLFSSL_METHOD *req_method = NULL; - struct alpn_spec alpns; - char *curves; -#ifdef WOLFSSL_HAVE_KYBER - word16 pqkem = 0; - size_t idx = 0; -#endif - CURLcode result = CURLE_FAILED_INIT; - unsigned char transport; - int tls_min, tls_max; - - DEBUGASSERT(!wctx->ssl_ctx); - DEBUGASSERT(!wctx->ssl); - conn_config = Curl_ssl_cf_get_primary_config(cf); - if(!conn_config) { - result = CURLE_FAILED_INIT; - goto out; - } - Curl_alpn_copy(&alpns, alpns_requested); - DEBUGASSERT(cf->next); - transport = Curl_conn_cf_get_transport(cf->next, data); - -#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ - req_method = wolfSSLv23_client_method(); -#else - req_method = wolfTLS_client_method(); -#endif - if(!req_method) { - failf(data, "wolfSSL: could not create a client method"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - if(wctx->ssl_ctx) - wolfSSL_CTX_free(wctx->ssl_ctx); - - wctx->ssl_ctx = wolfSSL_CTX_new(req_method); - if(!wctx->ssl_ctx) { - failf(data, "wolfSSL: could not create a context"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - result = ssl_version(data, conn_config, wctx, &tls_min, &tls_max); - if(result) - goto out; - -#ifndef WOLFSSL_TLS13 - { - const char *ciphers = conn_config->cipher_list; - if(ciphers) { - if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) { - failf(data, "failed setting cipher list: %s", ciphers); - result = CURLE_SSL_CIPHER; - goto out; - } - infof(data, "Cipher selection: %s", ciphers); - } - } -#else +#ifdef WOLFSSL_TLS13 #define MAX_CIPHER_LEN 4096 +#endif + +static CURLcode wssl_init_ciphers(struct Curl_easy *data, + struct wssl_ctx *wctx, + struct ssl_primary_config *conn_config, + int tls_min, int tls_max) +{ +#ifndef WOLFSSL_TLS13 + const char *ciphers = conn_config->cipher_list; + (void)tls_min; + (void)tls_max; + if(ciphers) { + if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) { + failf(data, "failed setting cipher list: %s", ciphers); + return CURLE_SSL_CIPHER; + } + infof(data, "Cipher selection: %s", ciphers); + } + return CURLE_OK; +#else + CURLcode result = CURLE_OK; if(conn_config->cipher_list || conn_config->cipher_list13) { const char *ciphers12 = conn_config->cipher_list; const char *ciphers13 = conn_config->cipher_list13; @@ -1173,45 +1123,276 @@ else result = wssl_add_default_ciphers(FALSE, &c); } - if(result) - goto out; - - if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) { - failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c)); - curlx_dyn_free(&c); - result = CURLE_SSL_CIPHER; - goto out; + if(!result) { + if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) { + failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c)); + result = CURLE_SSL_CIPHER; + } + else + infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c)); } - infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c)); curlx_dyn_free(&c); } + return result; #endif +} - curves = conn_config->curves; +static CURLcode wssl_init_curves(struct Curl_easy *data, + struct wssl_ctx *wctx, + struct ssl_primary_config *conn_config, + unsigned char transport +#ifdef WOLFSSL_HAVE_KYBER + , word16 *out_pqkem +#endif + ) +{ + char *curves = conn_config->curves; if(!curves && (transport == TRNSPRT_QUIC)) curves = (char *)CURL_UNCONST(QUIC_GROUPS); if(curves) { #ifdef WOLFSSL_HAVE_KYBER + size_t idx; for(idx = 0; gnm[idx].name != NULL; idx++) { - if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) { - pqkem = gnm[idx].group; + if(!strncmp(curves, gnm[idx].name, strlen(gnm[idx].name))) { + *out_pqkem = gnm[idx].group; break; } } - if(pqkem == 0) + if(*out_pqkem == 0) #endif { if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) { failf(data, "failed setting curves list: '%s'", curves); - result = CURLE_SSL_CIPHER; - goto out; + return CURLE_SSL_CIPHER; + } + } + } + return CURLE_OK; +} + +static CURLcode wssl_init_ssl_handle(struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + struct alpn_spec *alpns, + void *ssl_user_data, + unsigned char transport, +#ifdef WOLFSSL_HAVE_KYBER + word16 pqkem, +#endif + Curl_wssl_init_session_reuse_cb + *sess_reuse_cb) +{ + /* Let's make an SSL structure */ + wctx->ssl = wolfSSL_new(wctx->ssl_ctx); + if(!wctx->ssl) { + failf(data, "SSL: could not create a handle"); + return CURLE_OUT_OF_MEMORY; + } + +#ifdef HAVE_EX_DATA + wolfSSL_set_app_data(wctx->ssl, ssl_user_data); +#else + (void)ssl_user_data; +#endif +#ifdef WOLFSSL_QUIC + if(transport == TRNSPRT_QUIC) + wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0); +#else + (void)transport; +#endif + +#ifdef WOLFSSL_HAVE_KYBER + if(pqkem) { + if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != + WOLFSSL_SUCCESS) { + failf(data, "unable to use PQ KEM"); + } + } +#endif + + /* Check if there is a cached ID we can/should use here! */ + if(Curl_ssl_scache_use(cf, data)) { + /* Set session from cache if there is one */ + (void)wssl_setup_session(cf, data, wctx, alpns, peer->scache_key, + sess_reuse_cb); + } + +#ifdef HAVE_ALPN + if(alpns->count) { + struct alpn_proto_buf proto; + memset(&proto, 0, sizeof(proto)); + Curl_alpn_to_proto_str(&proto, alpns); + + if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data, + (unsigned int)proto.len, + WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) + != WOLFSSL_SUCCESS) { + failf(data, "SSL: failed setting ALPN protocols"); + return CURLE_SSL_CONNECT_ERROR; + } + CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data); + } +#endif /* HAVE_ALPN */ + +#ifdef OPENSSL_EXTRA + if(Curl_tls_keylog_enabled()) { + /* Ensure the Client Random is preserved. */ + wolfSSL_KeepArrays(wctx->ssl); +#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) + wolfSSL_set_tls13_secret_cb(wctx->ssl, wssl_tls13_secret_callback, NULL); +#endif + } +#endif /* OPENSSL_EXTRA */ + +#ifdef HAVE_SECURE_RENEGOTIATION + if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) { + failf(data, "SSL: failed setting secure renegotiation"); + return CURLE_SSL_CONNECT_ERROR; + } +#endif /* HAVE_SECURE_RENEGOTIATION */ + + return CURLE_OK; +} + +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG +static CURLcode wssl_init_ech(struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + int trying_ech_now = 0; + + if(data->set.str[STRING_ECH_PUBLIC]) { + infof(data, "ECH: outername not (yet) supported" + " with wolfSSL"); + return CURLE_SSL_CONNECT_ERROR; + } + if(data->set.tls_ech == CURLECH_GREASE) { + infof(data, "ECH: GREASE is done by default by" + " wolfSSL: no need to ask"); + } + if(data->set.tls_ech & CURLECH_CLA_CFG && + data->set.str[STRING_ECH_CONFIG]) { + char *b64val = data->set.str[STRING_ECH_CONFIG]; + word32 b64len = 0; + + b64len = (word32)strlen(b64val); + if(b64len && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, + b64len) != WOLFSSL_SUCCESS) { + if(data->set.tls_ech & CURLECH_HARD) + return CURLE_SSL_CONNECT_ERROR; + } + else { + trying_ech_now = 1; + infof(data, "ECH: ECHConfig from command line"); + } + } + else { + const struct Curl_https_rrinfo *rinfo = + Curl_conn_dns_get_https(data, cf->sockindex); + + if(rinfo && rinfo->echconfiglist) { + const unsigned char *ecl = rinfo->echconfiglist; + size_t elen = rinfo->echconfiglist_len; + + infof(data, "ECH: ECHConfig from HTTPS RR"); + if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != + WOLFSSL_SUCCESS) { + infof(data, "ECH: wolfSSL_SetEchConfigs failed"); + if(data->set.tls_ech & CURLECH_HARD) { + return CURLE_SSL_CONNECT_ERROR; + } + } + else { + trying_ech_now = 1; + infof(data, "ECH: imported ECHConfigList of length %zu", elen); + } + } + else { + infof(data, "ECH: requested but no ECHConfig available"); + if(data->set.tls_ech & CURLECH_HARD) { + return CURLE_SSL_CONNECT_ERROR; } } } - result = client_certificate(data, ssl_config, wctx); + if(trying_ech_now && + wolfSSL_set_min_proto_version(wctx->ssl, TLS1_3_VERSION) != 1) { + infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); + return CURLE_SSL_CONNECT_ERROR; + } + return CURLE_OK; +} +#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ + +CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, + struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_peer *peer, + const struct alpn_spec *alpns_requested, + Curl_wssl_ctx_setup_cb *cb_setup, + void *cb_user_data, + void *ssl_user_data, + Curl_wssl_init_session_reuse_cb *sess_reuse_cb) +{ + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_primary_config *conn_config; + WOLFSSL_METHOD *req_method = NULL; + struct alpn_spec alpns; +#ifdef WOLFSSL_HAVE_KYBER + word16 pqkem = 0; +#endif + CURLcode result = CURLE_FAILED_INIT; + unsigned char transport; + int tls_min, tls_max; + + DEBUGASSERT(!wctx->ssl_ctx); + DEBUGASSERT(!wctx->ssl); + conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!conn_config) { + result = CURLE_FAILED_INIT; + goto out; + } + Curl_alpn_copy(&alpns, alpns_requested); + DEBUGASSERT(cf->next); + transport = Curl_conn_cf_get_transport(cf->next, data); + + req_method = wolfTLS_client_method(); + if(!req_method) { + failf(data, "wolfSSL: could not create a client method"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + if(wctx->ssl_ctx) + wolfSSL_CTX_free(wctx->ssl_ctx); + + wctx->ssl_ctx = wolfSSL_CTX_new(req_method); + if(!wctx->ssl_ctx) { + failf(data, "wolfSSL: could not create a context"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + result = ssl_version(data, conn_config, wctx, &tls_min, &tls_max); + if(result) + goto out; + + result = wssl_init_ciphers(data, wctx, conn_config, tls_min, tls_max); + if(result) + goto out; + + result = wssl_init_curves(data, wctx, conn_config, transport +#ifdef WOLFSSL_HAVE_KYBER + , &pqkem +#endif + ); + if(result) + goto out; + + result = wssl_client_cert(data, ssl_config, wctx); if(result) goto out; @@ -1219,9 +1400,8 @@ * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with * SSL_get_verify_result() below. */ - wolfSSL_CTX_set_verify(wctx->ssl_ctx, - conn_config->verifypeer ? WOLFSSL_VERIFY_PEER : - WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_CTX_set_verify(wctx->ssl_ctx, conn_config->verifypeer ? + WOLFSSL_VERIFY_PEER : WOLFSSL_VERIFY_NONE, NULL); #ifdef HAVE_SNI if(peer->sni) { @@ -1268,7 +1448,7 @@ #ifdef NO_FILESYSTEM else if(conn_config->verifypeer) { failf(data, "SSL: Certificates cannot be loaded because wolfSSL was built" - " with \"no file system\". Either disable peer verification" + " with no file system. Either disable peer verification" " (insecure) or if you are building an application with libcurl you" " can load certificates via CURLOPT_SSL_CTX_FUNCTION."); result = CURLE_SSL_CONNECT_ERROR; @@ -1276,157 +1456,20 @@ } #endif - /* Let's make an SSL structure */ - wctx->ssl = wolfSSL_new(wctx->ssl_ctx); - if(!wctx->ssl) { - failf(data, "SSL: could not create a handle"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - -#ifdef HAVE_EX_DATA - wolfSSL_set_app_data(wctx->ssl, ssl_user_data); -#else - (void)ssl_user_data; -#endif -#ifdef WOLFSSL_QUIC - if(transport == TRNSPRT_QUIC) - wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0); -#endif - + result = wssl_init_ssl_handle(wctx, cf, data, peer, &alpns, ssl_user_data, + transport, #ifdef WOLFSSL_HAVE_KYBER - if(pqkem) { - if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != WOLFSSL_SUCCESS) { - failf(data, "unable to use PQ KEM"); - } - } + pqkem, #endif - - /* Check if there is a cached ID we can/should use here! */ - if(Curl_ssl_scache_use(cf, data)) { - /* Set session from cache if there is one */ - (void)wssl_setup_session(cf, data, wctx, &alpns, - peer->scache_key, sess_reuse_cb); - } - -#ifdef HAVE_ALPN - if(alpns.count) { - struct alpn_proto_buf proto; - memset(&proto, 0, sizeof(proto)); - Curl_alpn_to_proto_str(&proto, &alpns); - - if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data, - (unsigned int)proto.len, - WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) { - failf(data, "SSL: failed setting ALPN protocols"); - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data); - } -#endif /* HAVE_ALPN */ - -#ifdef OPENSSL_EXTRA - if(Curl_tls_keylog_enabled()) { - /* Ensure the Client Random is preserved. */ - wolfSSL_KeepArrays(wctx->ssl); -#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) - wolfSSL_set_tls13_secret_cb(wctx->ssl, wssl_tls13_secret_callback, NULL); -#endif - } -#endif /* OPENSSL_EXTRA */ - -#ifdef HAVE_SECURE_RENEGOTIATION - if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) { - failf(data, "SSL: failed setting secure renegotiation"); - result = CURLE_SSL_CONNECT_ERROR; + sess_reuse_cb); + if(result) goto out; - } -#endif /* HAVE_SECURE_RENEGOTIATION */ #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG - if(ECH_ENABLED(data)) { - int trying_ech_now = 0; - - if(data->set.str[STRING_ECH_PUBLIC]) { - infof(data, "ECH: outername not (yet) supported with wolfSSL"); - result = CURLE_SSL_CONNECT_ERROR; + if(CURLECH_ENABLED(data)) { + result = wssl_init_ech(wctx, cf, data); + if(result) goto out; - } - if(data->set.tls_ech == CURLECH_GREASE) { - infof(data, "ECH: GREASE is done by default by wolfSSL: no need to ask"); - } - if(data->set.tls_ech & CURLECH_CLA_CFG && - data->set.str[STRING_ECH_CONFIG]) { - char *b64val = data->set.str[STRING_ECH_CONFIG]; - word32 b64len = 0; - - b64len = (word32)strlen(b64val); - if(b64len && - wolfSSL_SetEchConfigsBase64(wctx->ssl, - b64val, b64len) != WOLFSSL_SUCCESS) { - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - trying_ech_now = 1; - infof(data, "ECH: ECHConfig from command line"); - } - } - else { - struct ssl_connect_data *connssl = cf->ctx; - struct Curl_dns_entry *dns = NULL; - - dns = Curl_dnscache_get(data, connssl->peer.hostname, connssl->peer.port, - cf->conn->ip_version); - if(!dns) { - infof(data, "ECH: requested but no DNS info available"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - struct Curl_https_rrinfo *rinfo = NULL; - - rinfo = dns->hinfo; - if(rinfo && rinfo->echconfiglist) { - unsigned char *ecl = rinfo->echconfiglist; - size_t elen = rinfo->echconfiglist_len; - - infof(data, "ECH: ECHConfig from DoH HTTPS RR"); - if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) != - WOLFSSL_SUCCESS) { - infof(data, "ECH: wolfSSL_SetEchConfigs failed"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - else { - trying_ech_now = 1; - infof(data, "ECH: imported ECHConfigList of length %ld", elen); - } - } - else { - infof(data, "ECH: requested but no ECHConfig available"); - if(data->set.tls_ech & CURLECH_HARD) { - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } - } - Curl_resolv_unlink(data, &dns); - } - } - - if(trying_ech_now && wolfSSL_set_min_proto_version(wctx->ssl, - TLS1_3_VERSION) != 1) { - infof(data, "ECH: cannot force TLSv1.3 [ERROR]"); - result = CURLE_SSL_CONNECT_ERROR; - goto out; - } } #endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ @@ -1444,6 +1487,21 @@ return result; } +bool Curl_wssl_need_httpsrr(struct Curl_easy *data) +{ +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + if(!CURLECH_ENABLED(data)) + return FALSE; + if((data->set.tls_ech & CURLECH_GREASE) || + (data->set.tls_ech & CURLECH_CLA_CFG)) + return FALSE; + return TRUE; +#else + (void)data; + return FALSE; +#endif +} + /* * This function loads all the client/CA certificates and CRLs. Setup the TLS * layer and do all necessary magic. @@ -1461,9 +1519,8 @@ if(connssl->state == ssl_connection_complete) return CURLE_OK; - result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer, - connssl->alpn, NULL, NULL, cf, - wssl_on_session_reuse); + result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer, connssl->alpn, + NULL, NULL, cf, wssl_on_session_reuse); if(result) return result; @@ -1730,7 +1787,7 @@ } else if(DOMAIN_NAME_MISMATCH == detail) { /* There is no easy way to override only the CN matching. - * This will enable the override of both mismatching SubjectAltNames + * This enables the override of both mismatching SubjectAltNames * as also mismatching CN fields */ failf(data, " subject alt name(s) or common name do not match \"%s\"", connssl->peer.dispname); @@ -2014,13 +2071,13 @@ case WOLFSSL_ERROR_NONE: case WOLFSSL_ERROR_WANT_READ: case WOLFSSL_ERROR_WANT_WRITE: - if(!wssl->io_result && connssl->peer_closed) { - CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen); - return CURLE_OK; + if(!wssl->io_result && !connssl->peer_closed) { + /* there is data pending, re-invoke wolfSSL_read() */ + CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); + return CURLE_AGAIN; } - /* there is data pending, re-invoke wolfSSL_read() */ - CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); - return CURLE_AGAIN; + /* fall through to default error handling below */ + FALLTHROUGH(); default: if(wssl->io_result == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); @@ -2047,11 +2104,7 @@ size_t Curl_wssl_version(char *buffer, size_t size) { -#if LIBWOLFSSL_VERSION_HEX >= 0x03006000 return curl_msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version()); -#elif defined(WOLFSSL_VERSION) - return curl_msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION); -#endif } static int wssl_init(void) @@ -2095,13 +2148,9 @@ void Curl_wssl_report_handshake(struct Curl_easy *data, struct wssl_ctx *wssl) { (void)wssl; -#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) infof(data, "SSL connection using %s / %s", wolfSSL_get_version(wssl->ssl), wolfSSL_get_cipher_name(wssl->ssl)); -#else - infof(data, "SSL connected"); -#endif } static CURLcode wssl_connect(struct Curl_cfilter *cf, @@ -2122,6 +2171,15 @@ connssl->io_need = CURL_SSL_IO_NEED_NONE; if(ssl_connect_1 == connssl->connecting_state) { +#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG + /* if we do ECH and need the HTTPS-RR information for it, + * we delay the connect until it arrives or DNS resolve fails. */ + if(Curl_wssl_need_httpsrr(data) && + !Curl_conn_dns_resolved_https(data, cf->sockindex)) { + CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect"); + return CURLE_OK; + } +#endif /* HAVE_WOLFSSL_CTX_GENERATEECHCONFIG */ result = wssl_connect_step1(cf, data); if(result) return result; @@ -2142,8 +2200,8 @@ } if(ssl_connect_3 == connssl->connecting_state) { - /* Once the handshake has errored, it stays in that state and will - * error again on every call. */ + /* Once the handshake has errored, it stays in that state and + * errors again on every call. */ if(wssl->hs_result) { result = wssl->hs_result; goto out;
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.h b/Utilities/cmcurl/lib/vtls/wolfssl.h index b097b60..2490bf3 100644 --- a/Utilities/cmcurl/lib/vtls/wolfssl.h +++ b/Utilities/cmcurl/lib/vtls/wolfssl.h
@@ -29,6 +29,8 @@ #include "urldata.h" +#include <wolfssl/options.h> + struct alpn_spec; struct ssl_peer; struct Curl_ssl_session; @@ -65,12 +67,15 @@ struct Curl_cfilter *cf, struct Curl_easy *data, struct ssl_peer *peer, - const struct alpn_spec *alpns, + const struct alpn_spec *alpns_requested, Curl_wssl_ctx_setup_cb *cb_setup, void *cb_user_data, void *ssl_user_data, Curl_wssl_init_session_reuse_cb *sess_reuse_cb); +/* Is a resolved HTTPS-RR needed for initializing wolfSSL? */ +bool Curl_wssl_need_httpsrr(struct Curl_easy *data); + CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, struct Curl_easy *data, struct wssl_ctx *wssl);
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c index b687a60..788dfb2 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.c +++ b/Utilities/cmcurl/lib/vtls/x509asn1.c
@@ -51,41 +51,6 @@ #define CURL_ASN1_MAX ((size_t)0x40000) /* 256K */ #ifdef WANT_EXTRACT_CERTINFO -/* ASN.1 classes. */ -/* #define CURL_ASN1_UNIVERSAL 0 */ -/* #define CURL_ASN1_APPLICATION 1 */ -/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */ -/* #define CURL_ASN1_PRIVATE 3 */ - -/* ASN.1 types. */ -#define CURL_ASN1_BOOLEAN 1 -#define CURL_ASN1_INTEGER 2 -#define CURL_ASN1_BIT_STRING 3 -#define CURL_ASN1_OCTET_STRING 4 -#define CURL_ASN1_NULL 5 -#define CURL_ASN1_OBJECT_IDENTIFIER 6 -/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */ -/* #define CURL_ASN1_INSTANCE_OF 8 */ -/* #define CURL_ASN1_REAL 9 */ -#define CURL_ASN1_ENUMERATED 10 -/* #define CURL_ASN1_EMBEDDED 11 */ -#define CURL_ASN1_UTF8_STRING 12 -/* #define CURL_ASN1_RELATIVE_OID 13 */ -/* #define CURL_ASN1_SEQUENCE 16 */ -/* #define CURL_ASN1_SET 17 */ -#define CURL_ASN1_NUMERIC_STRING 18 -#define CURL_ASN1_PRINTABLE_STRING 19 -#define CURL_ASN1_TELETEX_STRING 20 -/* #define CURL_ASN1_VIDEOTEX_STRING 21 */ -#define CURL_ASN1_IA5_STRING 22 -#define CURL_ASN1_UTC_TIME 23 -#define CURL_ASN1_GENERALIZED_TIME 24 -/* #define CURL_ASN1_GRAPHIC_STRING 25 */ -#define CURL_ASN1_VISIBLE_STRING 26 -/* #define CURL_ASN1_GENERAL_STRING 27 */ -#define CURL_ASN1_UNIVERSAL_STRING 28 -/* #define CURL_ASN1_CHARACTER_STRING 29 */ -#define CURL_ASN1_BMP_STRING 30 /* ASN.1 OID table entry. */ struct Curl_OID { @@ -160,10 +125,6 @@ * Please note there is no pretension here to rewrite a full SSL library. */ -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) - WARN_UNUSED_RESULT; - #define CURL_ASN1_MAX_RECURSIONS 16 static const char *getASN1Element_(struct Curl_asn1Element *elem, @@ -199,32 +160,35 @@ b = (unsigned char)*beg++; if(!(b & 0x80)) len = b; - else if(!(b &= 0x7F)) { - /* Unspecified length. Since we have all the data, we can determine the - effective length by skipping element until an end element is found. */ - if(!elem->constructed) - return NULL; - elem->beg = beg; - while(beg < end && *beg) { - beg = getASN1Element_(&lelem, beg, end, lvl + 1); - if(!beg) - return NULL; - } - if(beg >= end) - return NULL; - elem->end = beg; - return beg + 1; - } - else if((unsigned)b > (size_t)(end - beg)) - return NULL; /* Does not fit in source. */ else { - /* Get long length. */ - len = 0; - do { - if(len & 0xFF000000L) - return NULL; /* Lengths > 32 bits are not supported. */ - len = (len << 8) | (unsigned char) *beg++; - } while(--b); + b &= 0x7F; + if(!b) { + /* Unspecified length. Since we have all the data, we can determine the + effective length by skipping element until an end element is found. */ + if(!elem->constructed) + return NULL; + elem->beg = beg; + while(beg < end && *beg) { + beg = getASN1Element_(&lelem, beg, end, lvl + 1); + if(!beg) + return NULL; + } + if(beg >= end) + return NULL; + elem->end = beg; + return beg + 1; + } + else if((unsigned)b > (size_t)(end - beg)) + return NULL; /* Does not fit in source. */ + else { + /* Get long length. */ + len = 0; + do { + if(len & 0xFF000000L) + return NULL; /* Lengths > 32 bits are not supported. */ + len = (len << 8) | (unsigned char) *beg++; + } while(--b); + } } if(len > (size_t)(end - beg)) return NULL; /* Element data does not fit in source. */ @@ -233,8 +197,13 @@ return elem->end; } -static const char *getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) +/* + * @unittest 1657 + */ +UNITTEST const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end); +UNITTEST const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) { return getASN1Element_(elem, beg, end, 0); } @@ -255,17 +224,6 @@ return NULL; } -#ifdef UNITTESTS -/* used by unit1657.c */ -CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end) -{ - if(getASN1Element(elem, beg, end)) - return CURLE_OK; - return CURLE_BAD_FUNCTION_ARGUMENT; -} -#endif - /* * Convert an ASN.1 Boolean value into its string representation. * @@ -275,9 +233,13 @@ static CURLcode bool2str(struct dynbuf *store, const char *beg, const char *end) { + uint8_t val; if(end - beg != 1) return CURLE_BAD_FUNCTION_ARGUMENT; - return curlx_dyn_add(store, *beg ? "TRUE" : "FALSE"); + val = (uint8_t)*beg; + if((val != 0xff) && (val != 0x00)) + return CURLE_BAD_FUNCTION_ARGUMENT; + return curlx_dyn_add(store, val ? "TRUE" : "FALSE"); } /* @@ -300,7 +262,12 @@ { /* Convert an ASN.1 bit string to a printable string. */ - if(++beg > end) + if(beg < end) { + uint8_t unused_bits = (uint8_t)*beg++; + if(unused_bits > 7) + return CURLE_BAD_FUNCTION_ARGUMENT; + } + else return CURLE_BAD_FUNCTION_ARGUMENT; return octet2str(store, beg, end); } @@ -312,8 +279,11 @@ */ static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end) { - unsigned int val = 0; + uint32_t uval = 0; + int32_t sval = 0; size_t n = end - beg; + size_t i; + const unsigned char *p = (const unsigned char *)beg; if(!n) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -321,14 +291,20 @@ if(n > 4) return octet2str(store, beg, end); - /* Represent integers <= 32-bit as a single value. */ - if(*beg & 0x80) - val = ~val; + if(p[0] & 0x80) + uval = ~0U; - do - val = (val << 8) | *(const unsigned char *)beg++; - while(beg < end); - return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val); + for(i = 0; i < n; i++) + uval = (uval << 8) | p[i]; + + sval = (int32_t)uval; + + /* small values are easier to read as decimals */ + if(sval > -10000 && sval < 10000) + return curlx_dyn_addf(store, "%d", sval); + + /* represent larger values as hex */ + return curlx_dyn_addf(store, "0x%x", uval); } /* @@ -417,33 +393,66 @@ } /* - * Convert an ASN.1 OID into its dotted string representation. + * Convert an ASN.1 OID into its dotted string representation. Each field is + * limited to unsigned 32-bit values. + * + * A minor limitation is in the second number, which can be no larger than 32 + * bits - 80 == 4294967215. * * Return error code. + * + * @unittest 1666 */ -static CURLcode encodeOID(struct dynbuf *store, - const char *beg, const char *end) +UNITTEST CURLcode encodeOID(struct dynbuf *store, + const char *beg, const char *end); +UNITTEST CURLcode encodeOID(struct dynbuf *store, + const char *beg, const char *end) { - unsigned int x; - unsigned int y; + uint32_t x; + uint32_t y; CURLcode result = CURLE_OK; - /* Process the first two numbers. */ - y = *(const unsigned char *)beg++; - x = y / 40; - y -= x * 40; + if(end <= beg) + return CURLE_BAD_FUNCTION_ARGUMENT; - result = curlx_dyn_addf(store, "%u.%u", x, y); + /* Process the first two numbers. The initial digit cannot be larger than + 2 */ + y = *(const unsigned char *)beg++; + if(y <= 80) + x = y / 40; + else + x = 2; + result = curlx_dyn_addf(store, "%u", x); + if(result) + return result; + + if(y & 0x80) { + uint32_t t = (y & 0x7f); /* the second number */ + if(!t) + /* reject leading 0x80 padding */ + return CURLE_BAD_FUNCTION_ARGUMENT; + do { + if((t & 0xFE000000) || (beg >= end)) + return CURLE_BAD_FUNCTION_ARGUMENT; + y = *(const unsigned char *)beg++; + t = (t << 7) | (y & 0x7F); + } while(y & 0x80); + y = t; + } + + result = curlx_dyn_addf(store, ".%u", y - (x * 40)); if(result) return result; /* Process the trailing numbers. */ - while(beg < end) { + while((beg < end) && !result) { x = 0; + y = 0; do { - if(x & 0xFF000000) - return CURLE_OK; - else if(beg == end) + /* 0x80 as the first value is invalid since it only adds padding */ + if((y & 0x80) && !x) + return CURLE_BAD_FUNCTION_ARGUMENT; + else if((x & 0xFE000000) || (beg == end)) return CURLE_BAD_FUNCTION_ARGUMENT; y = *(const unsigned char *)beg++; x = (x << 7) | (y & 0x7F); @@ -460,32 +469,33 @@ */ static CURLcode OID2str(struct dynbuf *store, - const char *beg, const char *end, bool symbolic) + const char *beg, const char *end) { CURLcode result = CURLE_OK; if(beg < end) { - if(symbolic) { - struct dynbuf buf; - curlx_dyn_init(&buf, CURL_X509_STR_MAX); - result = encodeOID(&buf, beg, end); + struct dynbuf buf; + curlx_dyn_init(&buf, CURL_X509_STR_MAX); + result = encodeOID(&buf, beg, end); - if(!result) { - const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf)); - if(op) - result = curlx_dyn_add(store, op->textoid); - else - result = curlx_dyn_add(store, curlx_dyn_ptr(&buf)); - } - curlx_dyn_free(&buf); + if(!result) { + const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf)); + if(op) + result = curlx_dyn_add(store, op->textoid); + else + result = curlx_dyn_add(store, curlx_dyn_ptr(&buf)); } - else - result = encodeOID(store, beg, end); + curlx_dyn_free(&buf); } return result; } -static CURLcode GTime2str(struct dynbuf *store, - const char *beg, const char *end) +/* + * @unittest 1656 + */ +UNITTEST CURLcode GTime2str(struct dynbuf *store, + const char *beg, const char *end); +UNITTEST CURLcode GTime2str(struct dynbuf *store, + const char *beg, const char *end) { const char *tzp; const char *fracp; @@ -562,15 +572,6 @@ sep, (int)tzl, tzp); } -#ifdef UNITTESTS -/* used by unit1656.c */ -CURLcode Curl_x509_GTime2str(struct dynbuf *store, - const char *beg, const char *end) -{ - return GTime2str(store, beg, end); -} -#endif - /* * Convert an ASN.1 UTC time to a printable string. * @@ -604,11 +605,9 @@ tzp = "GMT"; end = tzp + 3; } - else - tzp++; tzl = end - tzp; - return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", + return curlx_dyn_addf(store, "%d%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", 20 - (*beg >= '5'), beg, beg + 2, beg + 4, beg + 6, beg + 8, sec, (int)tzl, tzp); @@ -618,17 +617,19 @@ * Convert an ASN.1 element to a printable string. * * Return error + * + * @unittest 1667 */ -static CURLcode ASN1tostr(struct dynbuf *store, - struct Curl_asn1Element *elem, int type) +UNITTEST CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem); +UNITTEST CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem) { CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + int type = elem->tag; if(elem->constructed) return result; /* No conversion of structured elements. */ - if(!type) - type = elem->tag; /* Type not forced: use element tag as type. */ - switch(type) { case CURL_ASN1_BOOLEAN: result = bool2str(store, elem->beg, elem->end); @@ -647,7 +648,7 @@ result = curlx_dyn_addn(store, "", 1); break; case CURL_ASN1_OBJECT_IDENTIFIER: - result = OID2str(store, elem->beg, elem->end, TRUE); + result = OID2str(store, elem->beg, elem->end); break; case CURL_ASN1_UTC_TIME: result = UTime2str(store, elem->beg, elem->end); @@ -712,7 +713,7 @@ goto error; } curlx_dyn_reset(&temp); - result = ASN1tostr(&temp, &oid, 0); + result = ASN1tostr(&temp, &oid); if(result) goto error; @@ -747,7 +748,7 @@ goto error; /* Generate value. */ - result = ASN1tostr(store, &value, 0); + result = ASN1tostr(store, &value); if(result) goto error; curlx_dyn_reset(&temp); @@ -905,7 +906,7 @@ if(!p) return CURLE_BAD_FUNCTION_ARGUMENT; } - return OID2str(store, oid.beg, oid.end, TRUE); + return OID2str(store, oid.beg, oid.end); } /* @@ -956,7 +957,7 @@ /* Generate a certificate information record for the public key. */ - result = ASN1tostr(&out, elem, 0); + result = ASN1tostr(&out, elem); if(!result) { if(data->set.ssl.certinfo) result = ssl_push_certinfo_dyn(data, certnum, label, &out); @@ -1144,7 +1145,7 @@ } /* Serial number. */ - result = ASN1tostr(&out, &cert.serialNumber, 0); + result = ASN1tostr(&out, &cert.serialNumber); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1167,7 +1168,7 @@ curlx_dyn_reset(&out); /* Start Date. */ - result = ASN1tostr(&out, &cert.notBefore, 0); + result = ASN1tostr(&out, &cert.notBefore); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1178,7 +1179,7 @@ curlx_dyn_reset(&out); /* Expire Date. */ - result = ASN1tostr(&out, &cert.notAfter, 0); + result = ASN1tostr(&out, &cert.notAfter); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1209,7 +1210,7 @@ curlx_dyn_reset(&out); /* Signature. */ - result = ASN1tostr(&out, &cert.signature, 0); + result = ASN1tostr(&out, &cert.signature); if(result) goto done; if(data->set.ssl.certinfo) {
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h index 829dfd8..dda55ac 100644 --- a/Utilities/cmcurl/lib/vtls/x509asn1.h +++ b/Utilities/cmcurl/lib/vtls/x509asn1.h
@@ -26,12 +26,51 @@ ***************************************************************************/ #include "curl_setup.h" +/* forward-declare to make it visible in all builds */ +struct Curl_asn1Element; + #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ defined(USE_MBEDTLS) || defined(USE_RUSTLS) #include "cfilters.h" #include "urldata.h" +/* ASN.1 classes. */ +/* #define CURL_ASN1_UNIVERSAL 0 */ +/* #define CURL_ASN1_APPLICATION 1 */ +/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */ +/* #define CURL_ASN1_PRIVATE 3 */ + +/* ASN.1 types. */ +#define CURL_ASN1_BOOLEAN 1 +#define CURL_ASN1_INTEGER 2 +#define CURL_ASN1_BIT_STRING 3 +#define CURL_ASN1_OCTET_STRING 4 +#define CURL_ASN1_NULL 5 +#define CURL_ASN1_OBJECT_IDENTIFIER 6 +/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */ +/* #define CURL_ASN1_INSTANCE_OF 8 */ +/* #define CURL_ASN1_REAL 9 */ +#define CURL_ASN1_ENUMERATED 10 +/* #define CURL_ASN1_EMBEDDED 11 */ +#define CURL_ASN1_UTF8_STRING 12 +/* #define CURL_ASN1_RELATIVE_OID 13 */ +/* #define CURL_ASN1_SEQUENCE 16 */ +/* #define CURL_ASN1_SET 17 */ +#define CURL_ASN1_NUMERIC_STRING 18 +#define CURL_ASN1_PRINTABLE_STRING 19 +#define CURL_ASN1_TELETEX_STRING 20 +/* #define CURL_ASN1_VIDEOTEX_STRING 21 */ +#define CURL_ASN1_IA5_STRING 22 +#define CURL_ASN1_UTC_TIME 23 +#define CURL_ASN1_GENERALIZED_TIME 24 +/* #define CURL_ASN1_GRAPHIC_STRING 25 */ +#define CURL_ASN1_VISIBLE_STRING 26 +/* #define CURL_ASN1_GENERAL_STRING 27 */ +#define CURL_ASN1_UNIVERSAL_STRING 28 +/* #define CURL_ASN1_CHARACTER_STRING 29 */ +#define CURL_ASN1_BMP_STRING 30 + /* * Types. */ @@ -75,20 +114,6 @@ const char *beg, const char *end); CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, const char *beg, const char *end); - -#ifdef UNITTESTS -#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \ - defined(USE_RUSTLS) - -/* used by unit1656.c */ -CURLcode Curl_x509_GTime2str(struct dynbuf *store, - const char *beg, const char *end); -/* used by unit1657.c */ -CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, - const char *beg, const char *end); -#endif /* USE_GNUTLS || USE_SCHANNEL || USE_MBEDTLS || RUSTLS */ -#endif /* UNITTESTS */ - #endif /* USE_GNUTLS || USE_WOLFSSL || USE_SCHANNEL || USE_MBEDTLS || USE_RUSTLS */ #endif /* HEADER_CURL_X509ASN1_H */
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c index 6046561..d7840f1 100644 --- a/Utilities/cmcurl/lib/ws.c +++ b/Utilities/cmcurl/lib/ws.c
@@ -116,8 +116,8 @@ * and keep track of boundaries. */ struct websocket { struct Curl_easy *data; /* used for write callback handling */ - struct ws_decoder dec; /* decode of we frames */ - struct ws_encoder enc; /* decode of we frames */ + struct ws_decoder dec; /* decode of ws frames */ + struct ws_encoder enc; /* encode of ws frames */ struct bufq recvbuf; /* raw data from the server */ struct bufq sendbuf; /* raw data to be sent to the server */ struct curl_ws_frame recvframe; /* the current WS FRAME received */ @@ -564,7 +564,7 @@ dec->state = WS_DEC_INIT; break; default: - /* we covered all enums above, but some code analyzers are whimps */ + /* we covered all enums above, but some code analyzers are wimps */ result = CURLE_FAILED_INIT; } return result; @@ -758,7 +758,7 @@ } if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) { - failf(data, "[WS] decode ending with %zd frame bytes remaining", + failf(data, "[WS] decode ending with %zu frame bytes remaining", Curl_bufq_len(&ctx->buf)); return CURLE_RECV_ERROR; } @@ -1049,9 +1049,9 @@ if((curl_off_t)buflen > (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) { /* too large buflen beyond payload length of frame */ - failf(data, "[WS] unaligned frame size (sending %zu instead of %" - FMT_OFF_T ")", - buflen, ws->enc.payload_remain + ws->sendbuf_payload); + failf(data, "[WS] unaligned frame size (sending %zu instead of " + "%" FMT_OFF_T ")", buflen, + (curl_off_t)(ws->enc.payload_remain + ws->sendbuf_payload)); return CURLE_BAD_FUNCTION_ARGUMENT; } } @@ -1399,7 +1399,7 @@ if(result) goto out; DEBUGASSERT(nread == nwritten); - k->keepon &= ~KEEP_RECV; /* read no more content */ + CURL_REQ_CLEAR_RECV(data); /* read no more content */ } else { /* !connect_only */ if(data->set.method == HTTPREQ_PUT) { @@ -1409,7 +1409,7 @@ goto out; if(!data->set.ws_raw_mode) { - /* Add our client readerr encoding WS BINARY frames */ + /* Add our client reader encoding WS BINARY frames */ result = Curl_creader_create(&ws_enc_reader, data, &ws_cr_encode, CURL_CR_CONTENT_ENCODE); if(result) @@ -1423,10 +1423,10 @@ /* start over with sending */ data->req.eos_read = FALSE; data->req.upload_done = FALSE; - k->keepon |= KEEP_SEND; + CURL_REQ_SET_SEND(data); } - /* And pass any additional data to the writers */ + /* Then pass any additional data to the writers */ if(nread) { result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread); if(result) @@ -1527,16 +1527,16 @@ return curl_easy_recv(data, buf, buflen, pnread); } -CURLcode curl_ws_recv(CURL *d, void *buffer, - size_t buflen, size_t *nread, +CURLcode curl_ws_recv(CURL *curl, void *buffer, + size_t buflen, size_t *recv, const struct curl_ws_frame **metap) { - struct Curl_easy *data = d; + struct Curl_easy *data = curl; struct connectdata *conn; struct websocket *ws; struct ws_collect ctx; - *nread = 0; + *recv = 0; *metap = NULL; if(!GOOD_EASY_HANDLE(data) || (buflen && !buffer)) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -1609,10 +1609,10 @@ update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, ctx.payload_len, ctx.bufidx); *metap = &ws->recvframe; - *nread = ws->recvframe.len; + *recv = ws->recvframe.len; CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" FMT_OFF_T ", %" FMT_OFF_T " left)", - buflen, *nread, ws->recvframe.offset, + buflen, *recv, ws->recvframe.offset, ws->recvframe.bytesleft); /* all's well, try to send any pending control. we do not know * when the application will call `curl_ws_send()` again. */ @@ -1689,6 +1689,9 @@ CURLcode result = CURLE_OK; size_t nwritten; + if(!data) + return result; + (void)ws; while(buflen) { result = Curl_xfer_send(data, buffer, buflen, FALSE, &nwritten); @@ -1746,6 +1749,8 @@ if(result) return result; result = ws_send_raw_blocking(data, ws, buffer, buflen); + if(!result) + *pnwritten = buflen; } else { /* We need any pending data to be sent or EAGAIN this call. */ @@ -1760,7 +1765,7 @@ return result; } -CURLcode curl_ws_send(CURL *d, const void *buffer_arg, +CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, size_t buflen, size_t *sent, curl_off_t fragsize, unsigned int flags) @@ -1768,7 +1773,7 @@ struct websocket *ws; const uint8_t *buffer = buffer_arg; CURLcode result = CURLE_OK; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; size_t ndummy; size_t *pnsent = sent ? sent : &ndummy; @@ -1848,11 +1853,11 @@ return Curl_http_setup_conn(data, conn); } -const struct curl_ws_frame *curl_ws_meta(CURL *d) +const struct curl_ws_frame *curl_ws_meta(CURL *curl) { /* we only return something for websocket, called from within the callback when not using raw mode */ - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn && !data->set.ws_raw_mode) { struct websocket *ws; @@ -1863,16 +1868,17 @@ return NULL; } -CURL_EXTERN CURLcode curl_ws_start_frame(CURL *d, +CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, unsigned int flags, curl_off_t frame_len) { struct websocket *ws; CURLcode result = CURLE_OK; - struct Curl_easy *data = d; + struct Curl_easy *data = curl; if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; + if(data->set.ws_raw_mode) { failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled"); return CURLE_FAILED_INIT; @@ -1893,12 +1899,6 @@ goto out; } - if(data->set.ws_raw_mode) { - failf(data, "[WS] cannot start frame in raw mode"); - result = CURLE_SEND_ERROR; - goto out; - } - if(ws->enc.payload_remain) { failf(data, "[WS] previous frame not finished"); result = CURLE_SEND_ERROR; @@ -1915,7 +1915,7 @@ return result; } -static const struct Curl_protocol Curl_protocol_ws = { +const struct Curl_protocol Curl_protocol_ws = { ws_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ @@ -1930,7 +1930,7 @@ ZERO_NULL, /* disconnect */ Curl_http_write_resp, /* write_resp */ Curl_http_write_resp_hd, /* write_resp_hd */ - ZERO_NULL, /* connection_check */ + ZERO_NULL, /* connection_is_dead */ ZERO_NULL, /* attach connection */ Curl_http_follow, /* follow */ }; @@ -1938,13 +1938,13 @@ #else CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, - size_t *nread, + size_t *recv, const struct curl_ws_frame **metap) { (void)curl; (void)buffer; (void)buflen; - (void)nread; + (void)recv; (void)metap; return CURLE_NOT_BUILT_IN; } @@ -1980,32 +1980,3 @@ } #endif /* !CURL_DISABLE_WEBSOCKETS */ - -const struct Curl_scheme Curl_scheme_ws = { - "WS", /* scheme */ -#ifdef CURL_DISABLE_WEBSOCKETS - ZERO_NULL, -#else - &Curl_protocol_ws, -#endif - CURLPROTO_WS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL, - PORT_HTTP /* defport */ -} -; - -const struct Curl_scheme Curl_scheme_wss = { - "WSS", /* scheme */ -#if defined(CURL_DISABLE_WEBSOCKETS) || !defined(USE_SSL) - ZERO_NULL, -#else - &Curl_protocol_ws, -#endif - CURLPROTO_WSS, /* protocol */ - CURLPROTO_HTTP, /* family */ - PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */ - PROTOPT_USERPWDCTRL, - PORT_HTTPS /* defport */ -};
diff --git a/Utilities/cmcurl/lib/ws.h b/Utilities/cmcurl/lib/ws.h index 757eae0..71b5b3f 100644 --- a/Utilities/cmcurl/lib/ws.h +++ b/Utilities/cmcurl/lib/ws.h
@@ -25,11 +25,10 @@ ***************************************************************************/ #include "curl_setup.h" -extern const struct Curl_scheme Curl_scheme_ws; -extern const struct Curl_scheme Curl_scheme_wss; - #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP) +extern const struct Curl_protocol Curl_protocol_ws; + /* meta key for storing protocol meta at connection */ #define CURL_META_PROTO_WS_CONN "meta:proto:ws:conn"